Setup

conda env on cluster

# Create env on cluster with mamba
mamba create -y \
  -n fst_env_rhel \
  -c bioconda gatk4
conda activate fst_env_rhel
mamba install bcftools plink2 r-base r-essentials r-tidyverse r-units libgdal r-sf
# Export
conda env export \
  --no-builds \
  -f envs/fst_env_rhel.yml
# Activate
conda activate fst_env_rhel

renv

# Export env (to renv.lock file)
renv::init()
# To install packages on new system, or 'activate' the env: 
renv::restore()

Source libraries, functions and plotting parameters

library(here)

source(here::here("code", "scripts", "source.R"))

Download 1KG data

Download from FTP

wget \
  -r -p -k \
  --no-parent \
  -cut-dirs=5 \
  ftp://ftp.1000genomes.ebi.ac.uk/vol1/ftp/release/20130502/

Put filenames into list

find vcfs/ftp.1000genomes.ebi.ac.uk/ALL.chr*.vcf.gz \
  > human_traits_fst/data/20200205_vcfs.list

Merge VCFs

java -jar /nfs/software/birney/picard-2.9.0/picard.jar MergeVcfs \
  I=human_traits_fst/data/20200205_vcfs.list \
  O=vcfs/1kg_all.vcf.gz
# Exception in thread "main" java.lang.IllegalArgumentException: The contig entries in input file /hps/research1/birney/users/ian/rac_hyp/vcfs/ftp.1000genomes.ebi.ac.uk/ALL.chrMT.phase3_callmom-v0_4.20130502.genotypes.vcf.gz are not compatible with the others.

# So remove that one from list above
sed -i '/MT/d' human_traits_fst/data/20200205_vcfs.list

# run MergeVCFs again
java -jar /nfs/software/birney/picard-2.9.0/picard.jar MergeVcfs \
  I=human_traits_fst/data/20200205_vcfs.list \
  O=vcfs/1kg_all.vcf.gz
  
# Exception in thread "main" java.lang.IllegalArgumentException: The contig entries in input file /hps/research1/birney/users/ian/rac_hyp/vcfs/ftp.1000genomes.ebi.ac.uk/ALL.chrY.phase3_integrated_v2a.20130502.genotypes.vcf.gz are not compatible with the others.
sed -i '/chrY/d' human_traits_fst/data/20200205_vcfs.list

# run MergeVCFs again
java -jar /nfs/software/birney/picard-2.9.0/picard.jar MergeVcfs \
  I=human_traits_fst/data/20200205_vcfs.list \
  O=vcfs/1kg_all.vcf.gz
# SUCCESS

Obtain GWAS data from the GWAS Catalog https://www.ebi.ac.uk/gwas

Pull data for each trait

NOTE: Uncheck Include child trait data before downloading.

All documents downloaded via ‘Download Catalog data’ link, then collated and saved here: data/20210122_gwas_catalog.xlsx

Height

BMI

Educational attainment

Intelligence

IBD

Pigmentation

Read into list

file_in = here::here("data", "20210122_gwas_catalog.xlsx")
# Create vector of traits
traits = c("hei", "bmi", "edu", "int", "ibd", "pig")
names(traits) = traits
# Assign sheets to traits
sheets <- seq(1:11)
names(sheets) <- c("hei", "bmi", "edu", "int", "ibd", rep("pig", 6))

# get sheets
sheet_names <- readxl::excel_sheets(file_in)

# Create function to read in data
read_catalog_data <- function(path, target_sheet){
  # Read in data
  out = readxl::read_xlsx(path, sheet = target_sheet) %>% 
    dplyr::select(CHR = CHR_ID, 
                  POS = CHR_POS, 
                  SNP_AL = `STRONGEST SNP-RISK ALLELE`, 
                  P = `P-VALUE`, 
                  OR_OR_BETA = `OR or BETA`, 
                  MAPPED_TRAIT,
                  STUDY = `STUDY ACCESSION`,
                  SAMPLE = `INITIAL SAMPLE SIZE`) %>% 
    # Split SNP and risk allele into separate columns
    dplyr::mutate(TOP_SNP = stringr::str_split(SNP_AL, "-", simplify = T)[, 1],
                  RISK_ALLELE = stringr::str_split(SNP_AL, "-", simplify = T)[, 2]) %>% 
    # Reorder and select
    dplyr::select(CHR, POS, TOP_SNP, RISK_ALLELE, P, OR_OR_BETA, MAPPED_TRAIT, STUDY, SAMPLE)
  # Change variables to specific types
  out$CHR <- as.integer(out$CHR)
  out$POS <- as.numeric(out$POS)
  out$P <- as.numeric(out$P)
  # return DF
  return(out)
}

# Read in data
counter <- 0
data_list = lapply(traits, function(trait){
  # set counter 
  counter <<- counter + 1
  # set target file
  target_file = file_in
  # get target sheet
  target_sheet = sheets[names(sheets) == trait]
  length(target_sheet)
  # read in pigmentation data from multiple sheets and bind into single DF
  if (length(target_sheet) > 1){
    # loop over each sheet
    df <- lapply(target_sheet, function(sheet){
      out <- read_catalog_data(target_file,
                               target_sheet = sheet)
    })
    # set name of each DF to name of sheet (replacing spaces with underscores)
    names(df) = sheet_names[target_sheet] %>% 
      stringr::str_replace_all(" ", "_")
    # bind DFs into single DF
    df <- dplyr::bind_rows(df, .id = "PIG_PHENO")
  } 
  else {
    # read in other data
    df <- read_catalog_data(target_file,
                            target_sheet = target_sheet)
  }
  # Set PHENO column
  df$PHENO <- factor(trait, levels = trait_levels)
  # Recode PHENO
  df$PHENO = dplyr::recode(df$PHENO, !!!recode_vec)
  # Create list
  out = list()
  # Return DF as "raw"
  out[["raw"]] = df
  
  return(out)
})

# How many SNPs in raw data
lapply(data_list, function(x) nrow(x[["raw"]]))

# Clean data
data_list = lapply(data_list, function(pheno){
  df_clean = pheno[["raw"]]
  # Remove rows with p-value of 0 (only 32 of them, associated with suntan)
  df_clean = df_clean[df_clean$P != 0, ]
  # Remove rows with NA in CHR
  df_clean = df_clean[!is.na(df_clean$CHR), ]
  # Remove duplicates
  ## Find SNPs that are duplicated
  dupes = unique(df_clean$TOP_SNP[duplicated(df_clean$TOP_SNP) | duplicated(df_clean$TOP_SNP, fromLast = T)])
  ## Select only 1 SNP from each set of duplicated SNPs
  dupe_filt = lapply(dupes, function(dupe){
    # Take the one with the lowest P-value
    min_p = min(df_clean$P[df_clean$TOP_SNP == dupe])
    out = df_clean[df_clean$TOP_SNP == dupe & df_clean$P == min_p, ]
    # If there are still duplicates due to having equal P, take the one with the largest effect size
    if (nrow(out) > 1 ){
      out = out[which.max(out$OR_OR_BETA), ]
    }
    return(out)
  })
  # Bind list into DF
  dupe_filt = dplyr::bind_rows(dupe_filt)
  # Extract non-duplicated rows
  non_dupe = df_clean[!duplicated(df_clean$TOP_SNP) & !duplicated(df_clean$TOP_SNP, fromLast = T), ]
  # Bind non-duplicated rows with filtered duplicates
  df_clean = rbind(non_dupe, dupe_filt) 
  # Add to list
  pheno[["clean"]] = df_clean
  
  return(pheno)
})

# New SNP count
lapply(data_list, function(x) nrow(x[["clean"]]))
lapply(data_list, function(pheno){
  knitr::kable(head(pheno[["clean"]]))
})

Get unique mapped traits

lapply(data_list, function(pheno){
  unique(pheno$clean$MAPPED_TRAIT)
})

Are any of the OR_OR_BETAs negative?

any(unlist(lapply(data_list, function(pheno) {
  any(pheno$clean$OR_OR_BETA < 0,na.rm = T)
})))

This must means that the RISK_ALLELE always affects the trait positively.

But for what proportion of SNPs is the RISK_ALLELE not stated?

lapply(data_list, function(pheno) {
  length(which(pheno$clean$RISK_ALLELE == "?")) / nrow(pheno$clean)
})

NOTE: In cases where RISK_ALLELE isn’t provided, treat the ALT allele as RISK_ALLELE.

Generate Manhattan plots

Plot

Save

output_dir = here::here("plots", "20210122_manhattan_all_snps")

counter = 0
lapply(data_list, function(pheno_df){

  # set counter
  counter <<- counter + 1
  df = pheno_df[["clean"]]
  trait = unique(df$PHENO)
  # Get title
  title <- paste(trait, "\n", "SNP count:", nrow(df))
  # Set file name to save
  file = file.path(output_dir,
                   paste("manhattan_",
                         names(data_list)[counter],
                         ".svg",
                         sep = ""))
  # Set up graphics device
  svg(file,
      width = 10,
      height = 6)  
  
  # Plot
  get_man(df, trait = trait, chr = "CHR", bp = "POS", snp = "TOP_SNP", p = "P")
  
  dev.off()
})

Create list of target SNPs to extract from 1KG

dest_dir = here::here("data", "20210122_snp_hit_lists")

# Make directory
dir.create(dest_dir)
  
# Just SNPs for extracting from 1KG
counter <- 0
lapply(data_list, function(pheno){
  df = pheno[["clean"]]
  # Set counter
  counter <<- counter + 1
  # Set file basename
  trait = names(data_list)[counter]
  filename = paste(trait, ".list", sep = "")
  # Write SNPs to file
  readr::write_lines(df$TOP_SNP, file.path(dest_dir, filename))
})

# SNPs and P-values with header for clumping with Plink
counter <- 0
lapply(data_list, function(pheno){
  df = pheno[["clean"]]
  # Set counter
  counter <<- counter + 1
  # Set file basename
  trait = names(data_list)[counter]
  filename = paste(trait, "_with_P.txt", sep = "")
  # Write SNPs to file
  df %>% 
    dplyr::select(SNP = TOP_SNP, P) %>% 
    readr::write_tsv(file.path(dest_dir, filename))
})

Filter 1KG VCF for target SNPs

traits=$(echo hei bmi edu int ibd pig)
ref=../refs/hs37d5.fa.gz
in_vcf=../vcfs/1kg_all.vcf.gz
snps_dir=data/20210122_snp_hit_lists
out_dir=data/20210125_snp_hits_filtered

mkdir -p $out_dir

for trait in $(echo $traits ); do
  bsub \
    -M 10000 \
    -o ../log/20210122_extract_snps_$trait.out \
    -e ../log/20210122_extract_snps_$trait.err \
    """
    conda activate fst_env_rhel ;
    gatk SelectVariants \
      -R $ref \
      -V $in_vcf \
      --keep-ids $snps_dir/$trait.list \
      -O $out_dir/$trait.vcf.gz 
    """ ;
done

Get allele frequencies of SNP hits with Plink2

Import 1KG metadata (for sample-population key)

Downloded via this page: http://www.internationalgenome.org/data.

Download link: http://ftp.1000genomes.ebi.ac.uk/vol1/ftp/technical/working/20130606_sample_info/20130606_sample_info.xlsx.

Saved here: data/20130606_sample_info.xlsx

samples_file = here::here("data", "20130606_sample_info.xlsx")

meta = readxl::read_xlsx(samples_file,
                         sheet = "Sample Info") %>%
  dplyr::select(Sample, Population, Gender)

knitr::kable(head(meta))
Sample Population Gender
HG00096 GBR male
HG00097 GBR female
HG00098 GBR male
HG00099 GBR female
HG00100 GBR female
HG00101 GBR male

Write population file for Plink2

sample_popn_key_file = here::here("data", "plink2_sample_popn_key.txt")

write.table(meta[, 1:2],
            sample_popn_key_file,
            quote = F,
            sep = "\t",
            row.names = F,
            col.names = F)

Run Plink2 for SNP hits

(Take only biallelic SNPs.)

conda activate fst_env_rhel

# Set variables

traits=$(echo hei bmi edu int ibd pig)
in_vcf_dir=data/20210125_snp_hits_filtered
popn_file=data/plink2_sample_popn_key.txt
out_dir=data/20210122_snp_hits_alfreqs

# Set up directories
mkdir -p $out_dir

for trait in $(echo $traits); do
  mkdir -p $out_dir/$trait; 
done   

# Run Plink2

## Get global AF
for trait in $(echo $traits); do
  plink2 \
    --vcf $in_vcf_dir/$trait.vcf.gz \
    --freq \
    --max-alleles 2 \
    --snps-only \
    --out $out_dir/$trait/$trait.all
done

## Get AF per population
for trait in $(echo $traits); do
  plink2 \
    --vcf $in_vcf_dir/$trait.vcf.gz \
    --freq \
    --max-alleles 2 \
    --snps-only \
    --pheno iid-only $popn_file \
    --loop-cats PHENO1 \
    --out $out_dir/$trait/$trait ;
done

Add allele frequency files

target_dir = here::here("data", "20210122_snp_hits_alfreqs")

# Global
counter <- 0
data_list = lapply(data_list, function(pheno){
  # set counter 
  counter <<- counter + 1
  # get trait name
  trait = names(data_list)[counter]
  # get file path
  target_path = file.path(target_dir, trait, paste(trait, ".all.afreq", sep = ""))
  # read in data
  clean_af = read_afreq(target_path)
  # add POPN column
  clean_af$POPN = "all"
  # add to list
  pheno[["clean_af"]] = clean_af
  
  return(pheno)
})

# Per population
counter <- 0
data_list = lapply(data_list, function(pheno){
  # set counter
  counter <<- counter + 1
  # get trait name
  trait = names(data_list)[counter]
  # get file path
  target_files = list.files(file.path(target_dir, trait),
                            pattern = ".*[^all]\\.afreq",
                            full.names = T)
  # get popn names
  names(target_files) = basename(target_files) %>% 
    str_split("\\.", simplify = T) %>% 
    subset(select = 2)
  # read files and bind into single DF
  popn_afreqs = lapply(target_files, function(popn){
    df = read_afreq(popn)
  }) %>% 
    dplyr::bind_rows(.id = "POPN")# %>% 
#    dplyr::select(-OBS_CT) %>% 
#    tidyr::pivot_wider(id_cols = SNP, names_from = POPN, values_from = ALT_FREQS)
  # combine with `clean_af`
  pheno[["clean_af"]] = dplyr::bind_rows(pheno[["clean_af"]],
                                         popn_afreqs)
  
  return(pheno)
})

Set up negative controls

Pull out random SNPs with the same global allele frequencies as the GWAS SNP-hits

Bin SNP hits by allele frequency

Bind to clean DF to get AFs of risk allele

NOTE: If RISK_ALLELE is unknown, set the allele frequency to ALT_FREQS

data_list = lapply(data_list, function(pheno){
  # join DFs
  df = dplyr::left_join(pheno[["clean"]],
                        dplyr::select(pheno[["clean_af"]],
                                      -CHR),
                        by = c("TOP_SNP" = "SNP"))
  # get AF of risk allele
  df$RISK_AF = dplyr::if_else(df$RISK_ALLELE == "?",
                              df$ALT_FREQS,
                              dplyr::if_else(df$RISK_ALLELE == df$ALT,
                                             df$ALT_FREQS,
                                             1 - df$ALT_FREQS))
  # add `HIT_CONTROL` column
  df$HIT_CONTROL = "hit"
  # add to list
  pheno[["consol"]] = df
  
  return(pheno)
})

Bin by risk allele frequency

# 1% intervals
breakpoints = seq(0, 1, 0.01)

data_list = lapply(data_list, function(pheno){
  # choose DF
  df = pheno[["consol"]]
  # add bins
  df$BIN_100 = cut(df$RISK_AF, breaks = breakpoints, labels = F)
  # save back into list
  pheno[["consol"]] = df
  
  return(pheno)
})

Extract key columns and write to file

out_dir = here::here("data", "20210126_snp_risk_hits_binned")

dir.create(out_dir)

# Save list
counter <- 0
risk_afs = lapply(data_list, function(pheno){
  # set counter 
  counter <<- counter + 1
  # get target DF
  df = pheno[["consol"]]
  # filter
  df = df %>% 
    dplyr::filter(POPN == "all") %>% # take only global AFs 
    dplyr::select(TOP_SNP, BIN_100) %>% 
    tidyr::drop_na() # drop NAs
  # set output path
  trait = names(data_list)[counter]
  path_out = file.path(out_dir, paste(trait, ".txt", sep = ""))
  # write to file
  readr::write_tsv(df, path_out)
})

Bin 1KG SNPs

Get allele frequencies from 1KG

With Plink2, per chromosome for speed.

# set output directory
in_file=../vcfs/1kg_all.vcf.gz
out_dir=../big_data/20210125_alfreqs_all

mkdir -p $out_dir

# Per chromosome
for chr in $(seq 1 22) ; do
  # create allele-freq tables
  bsub \
    -M 10000 \
    -o ../log/20210125_plink_alfreq_$chr.out \
    -e ../log/20210125_plink_alfreq_$chr.err \
    """
    conda activate fst_env_rhel ;
    plink2 \
      --vcf $in_file \
      --freq \
      --chr $chr \
      --max-alleles 2 \
      --snps-only \
      --out $out_dir/$chr ";
done 

Bin them and save to single file

# On cluster

library(here)
source(here::here("code", "scripts", "source.R"))

# Set variables
in_dir = "../big_data/20210125_alfreqs_all"
out_dir = "../big_data/20210125_alfreqs_all_binned"
breakpoints = seq(0, 1, 0.01) # 1% bins

# Create output directory
dir.create(out_dir)

# Get list of input files
in_files = list.files(in_dir, pattern = ".afreq", full.names = T)

# Read in files, add bins, and write to output
freq_list = lapply(in_files, function(chr_file){
  # read in file
  df = read_afreq(chr_file)
  # add bins
  df$BIN_100 = cut(df$ALT_FREQS, breaks = breakpoints, labels = F)
  # write file
  readr::write_tsv(df, file = file.path(out_dir, basename(chr_file)))
})

# Combine into single DF
freq_df = dplyr::bind_rows(freq_list)

# Write to file
readr::write_tsv(freq_df, file = file.path(out_dir, "all.afreq"))

Pull out random SNPs with same AF as trait risk alleles

# On cluster

library(here)
source(here::here("code", "scripts", "source.R"))

# Variables

input_risk_snp_dir = here::here("data", "20210126_snp_risk_hits_binned")
all_1kg_bins = "../big_data/20210125_alfreqs_all_binned/all.afreq"
initial_seed = 123
output_dir = here::here("data", "20210126_random_snps")
output_dir_snpids = here::here("data", "20210126_random_snps_snp_ids")

dir.create(output_dir)
dir.create(output_dir_snpids)

## Read in target SNP DF and split into list by bin
phenos = gsub(".txt", "", basename(list.files(input_risk_snp_dir)))
names(phenos) = phenos

risk_list = lapply(phenos, function(pheno){
  # set file path
  file_path = file.path(input_risk_snp_dir, paste(pheno, ".txt", sep = ""))
  # read file
  out = readr::read_tsv(file_path,
                        col_names = T) %>% 
    split(., f = .$BIN_100) # split by bin
})
  
## Read in 1KG data
freq_df = readr::read_tsv(all_1kg_bins)

# For each bin in `risk_list`, pull out the same number of random number 1KG SNPs with the same bin

## Set seed
set.seed(initial_seed)

## Get seeds for each bin
seeds = sample(1:1000, length(risk_list))

## Run over list
counter <- 0
lapply(risk_list, function(pheno){
  # set counter 
  counter <<- counter + 1  
  # set seed for pheno
  set.seed(seeds)[counter]
  # get seeds for bin
  bin_seeds = sample(1:1000, length(pheno))
  # get random SNPs from each bin
  bin_counter <- 0
  out = lapply(pheno, function(bin_df){
    # set `bin_counter`
    bin_counter <<- bin_counter + 1
    # get target bin
    target_bin = as.integer(names(pheno)[bin_counter])
    # get number of matches required
    hits_n = nrow(bin_df)
    # set seed
    set.seed(bin_seeds[bin_counter])    
    # filter 1kg DF for SNPs with same bin and get random hits
    random_hits = freq_df %>% 
      #dplyr::select(SNP, ALT_FREQS, OBS_CT, BIN_100) %>% 
      dplyr::filter(BIN_100 == target_bin) %>% 
      dplyr::slice_sample(n = hits_n) %>% 
      dplyr::rename(RANDOM_SNP = SNP,
                    RANDOM_BIN_100 = BIN_100)
    # bind `random_hits` to target SNP df
    df_out = cbind(bin_df, random_hits)
    
    return(df_out)
  }) %>% 
    # bind into single data frame
    dplyr::bind_rows()
  
  # save to file
  ## set output path
  trait = names(risk_list)[counter]
  out_path = file.path(output_dir, paste(trait, ".txt", sep = ""))
  ## write file
  readr::write_tsv(out, out_path)
  
  # save just SNP IDs (for Plink to get per-population AFs)
  out_path = file.path(output_dir_snpids, paste(trait, ".list", sep = ""))
  ## write file
  readr::write_lines(out$RANDOM_SNP, out_path)  
})

Filter VCFs for random SNPs

traits=$(echo hei bmi edu int ibd pig)
ref=../refs/hs37d5.fa.gz
in_vcf=../vcfs/1kg_all.vcf.gz
snps_dir=data/20210126_random_snps_snp_ids
out_dir=data/20210127_snp_rndm_filtered

mkdir -p $out_dir

for trait in $(echo $traits ); do
  bsub \
    -M 10000 \
    -o ../log/20210127_extract_snps_$trait.out \
    -e ../log/20210127_extract_snps_$trait.err \
    """
    conda activate fst_env_rhel ;
    gatk SelectVariants \
      -R $ref \
      -V $in_vcf \
      --keep-ids $snps_dir/$trait.list \
      -O $out_dir/$trait.vcf.gz 
    """ ;
done

Get per-population allele frequencies of random SNPs

traits=$(echo hei bmi edu int ibd pig)
random_snps_dir=data/20210126_random_snps_snp_ids
vcf_in_dir=data/20210127_snp_rndm_filtered
popn_key=data/plink2_sample_popn_key.txt
out_dir=data/20210127_snp_rndm_alfreqs


for trait in $(echo $traits ); do

  mkdir -p $out_dir/$trait
  
  bsub \
    -M 10000 \
    -o ../log/20210127_rdm_popn_afreqs_$trait.out \
    -e ../log/20210127_rdm_popn_afreqs_$trait.err \
    """
    conda activate fst_env_rhel ;
    plink2 \
      --vcf $vcf_in_dir/$trait.vcf.gz \
      --extract $random_snps_dir/$trait.list \
      --freq \
      --pheno iid-only $popn_key \
      --loop-cats PHENO1 \
      --out $out_dir/$trait/$trait
    """ ;
done  

Bind into single DF

in_dir_rndm = here::here("data", "20210126_random_snps")
in_dir_afreq = here::here("data", "20210127_snp_rndm_alfreqs")

# Read in data

## Random SNPs
in_files_rndm = list.files(in_dir_rndm, full.names = T)
names(in_files_rndm) = gsub(".txt", "", basename(in_files_rndm))

random_snps = lapply(in_files_rndm, function(file){
  out = readr::read_tsv(file)
  # add `POPN` column
  out$POPN = "all"
  
  return(out)
}) %>% 
  dplyr::bind_rows(.id = "PHENO")

## Popn afreqs
popn_afreqs = lapply(trait_levels, function(pheno){
  target_files = list.files(file.path(in_dir_afreq, pheno), pattern = ".afreq", full.names = T)
  
  names(target_files) = basename(target_files) %>% 
    str_split("\\.", simplify = T) %>% 
    subset(select = 2)
  
  popn_afreqs = lapply(target_files, function(popn){
    df = read_afreq(popn)
  }) %>% 
    dplyr::bind_rows(.id = "POPN") # %>% 
  #  dplyr::select(-OBS_CT) %>% 
  #  tidyr::pivot_wider(id_cols = SNP, names_from = POPN, values_from = c(ALT_FREQS, OBS_CT))
  
  return(popn_afreqs)
}) %>% 
  dplyr::bind_rows(.id = "PHENO")

# Bind `popn_afreqs` to `random_snps`
random_afreqs = dplyr::full_join(dplyr::select(random_snps, -c(POPN, ALT_FREQS, OBS_CT)),
                                 popn_afreqs,
                                 by = c("PHENO", "RANDOM_SNP" = "SNP", "REF", "ALT", "CHR"))
# Bind `all` AFs
random_afreqs = rbind(random_afreqs, random_snps)

## Recode PHENO
#random_afreqs$PHENO = dplyr::recode(random_afreqs$PHENO, !!!rev_recode_vec)

## Add `HIT_CONTROL` column
random_afreqs$HIT_CONTROL = "control"

Add to data_list

counter <- 0
data_list = lapply(data_list, function(pheno){
  # set counter
  counter <<- counter + 1
  # set target pheno
  target_pheno = names(data_list)[counter]
  # add random afreqs
  random_af = random_afreqs %>% 
    dplyr::filter(PHENO == target_pheno)
  # recode PHENO
  random_af$PHENO = dplyr::recode(random_af$PHENO, !!!recode_vec)
  # add to list
  pheno[["random_af"]] = random_af
  
  return(pheno)
})

Clump to get lead SNPs

Use Plink1.9 (Plink2.0 doesn’t have a clump function.)

From the Plink1.7 documentation (http://zzz.bwh.harvard.edu/plink/clump.shtml), which applies to Plink1.9:

The clumping procedure takes all SNPs that are significant at threshold p1 that have not already been clumped (denoting these as index SNPs) and forms clumps of all other SNPs that are within a certain kb distance from the index SNP (default 250kb) and that are in linkage disequilibrium with the index SNP, based on an r-squared threshold (default 0.50)… This is a greedy algorithm and so each SNP will only appear in a single clump, if at all.

…[t]he TOTAL field lists all SNPs that are clumped with the index SNP, irrespective of the p-value for those SNPs. This number is then split into those clumped SNPs that are not significant (p>0.05) and various other groups defined by significance thresholds. For SNPs that are significant at the p2 threshold, they are listed explicitly. The (1) after each SNP name refers to the results file they came from (in this case, there is only a single result file specified, so all values are 1).

Here, we’re taking all SNPs with P < 1e-08 as index SNPs, and it will explicitly list all SNPs within the clump that also meet that threshold.

# Activate environment
conda activate fst_env_rhel

# Set variables
traits=$(echo hei bmi edu int ibd pig)
in_vcf_dir=data/20210125_snp_hits_filtered
snp_p_dir=data/20210122_snp_hit_lists
out_dir=data/20210125_clumped

r2_params=$(echo 0.1 0.2 0.3)
kb_params=$(echo 500 750 1000 )

# Make directory
mkdir -p $out_dir

# Run with different parameters
for trait in $(echo $traits ); do

  mkdir -p $out_dir/$trait ;
  
  for r2 in $r2_params ; do
    for kb in $kb_params ; do
      bsub \
        -o ../log/20210125_clump_$trait\_$r2\_$kb.out \
        -e ../log/20210125_clump_$trait\_$r2\_$kb.err \
        """
        conda activate fst_env_rhel ;
        plink \
          --vcf $in_vcf_dir/$trait.vcf.gz \
          --clump $snp_p_dir/$trait\_with_P.txt \
          --clump-p1 0.00000001 \
          --clump-p2 0.00000001 \
          --clump-r2 $r2 \
          --clump-kb $kb \
          --out $out_dir/$trait/r2-$r2\_kb-$kb 
        """  ;
    done ;
  done;  
done

Filter VCFs for clumped SNPs

# On cluster

# Set variables
traits=$(echo hei bmi edu int ibd pig)
clump_param="r2-0.1_kb-1000"
in_dir_snp=data/20210125_clumped
in_dir_vcf=data/20210125_snp_hits_filtered
ref=../refs/hs37d5.fa.gz
out_dir_snp_list=data/20210128_clumped_snps
out_dir_vcfs=data/20210128_hits_vcfs

mkdir -p $out_dir_snp_list $out_dir_vcfs

# Extract SNP column and write to list
for trait in $(echo $traits ); do
  target_file=$in_dir_snp/$trait/$clump_param.clumped ;
  awk '{print $3}' $target_file | tail -n+2 \
    > $out_dir_snp_list/$trait.list ;
done

# Make new VCFs
for trait in $(echo $traits ); do
  bsub \
    -o ../log/20210128_filter_vcfs_clump_$trait.out \
    -e ../log/20210128_filter_vcfs_clump_$trait.err \
    """
    conda activate fst_env_rhel ;
    gatk SelectVariants \
      -R $ref \
      -V $in_dir_vcf/$trait.vcf.gz \
      --keep-ids $out_dir_snp_list/$trait.list \
      -O $out_dir_vcfs/$trait.vcf.gz     
    """ ;
done    

Add clumped SNP files to data_list

target_dir = here::here("data", "20210125_clumped")

counter <- 0
data_list = lapply(data_list, function(pheno){
  # set counter
  counter <<- counter + 1
  # get trait name
  trait = names(data_list)[counter]
  # get file path
  target_files = list.files(file.path(target_dir, trait),
                            pattern = ".clumped",
                            full.names = T)
  names(target_files) = gsub(".clumped", "", basename(target_files))
  # read files as `clumped`
  counter_clump <- 0
  clumped = lapply(target_files, function(params){
    # set counter
    counter_clump <<- counter_clump + 1
    # split params string
    param_str = names(target_files)[counter_clump] %>% 
      stringr::str_split("_", simplify = T) %>%
      stringr::str_split("-", simplify = T)
    # get params
    r2 = as.numeric(param_str[1,2])
    kb = as.integer(param_str[2,2])
    # read files
    df = read.table(params, header = T)
    # add params to DF
    df$r2 = r2
    df$kb = kb
    
    return(df)
  })
  # add `clumped` list to `data_list`
  pheno[["clumped"]] = clumped
  # bind `clumped` into single DF and add to `data_list`
  pheno[["clumped_all"]] = dplyr::bind_rows(clumped)
  
  return(pheno)
})

Extract random SNPs filtered by those matching the clumped SNPs

out_dir = here::here("data", "20210128_random_snps")

dir.create(out_dir)

counter = 0
out = lapply(data_list, function(pheno){
  # set counter
  counter <<- counter + 1
  # get name of trait
  trait = names(data_list)[counter]
  # get clumped SNPs
  clumped_snps = pheno[["clumped"]][[clump_param]]$SNP
  # gather SNPs
  snps = pheno[["random_af"]] %>% 
    dplyr::filter(TOP_SNP %in% clumped_snps) %>% 
    dplyr::select(RANDOM_SNP) %>% 
    unique(.)
  # write to file
  out_path = file.path(out_dir, paste(trait, ".list", sep = ""))
  readr::write_lines(snps$RANDOM_SNP, out_path)
})
rm(out)

Make VCFs for random control SNPs

# On cluster

# Set variables
traits=$(echo hei bmi edu int ibd pig)
in_dir_snp_list=data/20210128_random_snps
in_vcf=../vcfs/1kg_all.vcf.gz
ref=../refs/hs37d5.fa.gz
out_dir_vcfs=data/20210128_rndm_vcfs

mkdir -p $out_dir_vcfs

# Make new VCFs
for trait in $(echo $traits ); do
  bsub \
    -M 20000 \
    -o ../log/20210128_filter_vcfs_rndm_$trait.out \
    -e ../log/20210128_filter_vcfs_rndm_$trait.err \
    """
    conda activate fst_env_rhel ;
    gatk SelectVariants \
      -R $ref \
      -V $in_vcf \
      --keep-ids $in_dir_snp_list/$trait.list \
      -O $out_dir_vcfs/$trait.vcf.gz     
    """ ;
done    

Consolidate key data into single DF for analysis

NOTE: clump_param is set in code/scripts/source.R

data_list = lapply(data_list, function(pheno){
  # Filter `consol` by the index SNPs in target `clump`
  target_clump = pheno[["clumped"]][[clump_param]]
  
  final = pheno[["consol"]] %>% 
    dplyr::rename(SNP = TOP_SNP) %>% 
    dplyr::filter(SNP %in% target_clump$SNP) 
  
  # Add allele frequencies of SNP hits (global and per-population)
  
  # Add controls
  controls = pheno[["random_af"]] %>%
    # filter for SNPs in target_clump
    dplyr::filter(TOP_SNP %in% target_clump$SNP) %>% 
    dplyr::select(-c(TOP_SNP, BIN_100, RANDOM_BIN_100), 
                  SNP = RANDOM_SNP) %>% 
    dplyr::mutate(RISK_AF = ALT_FREQS)
  
  final = dplyr::bind_rows(final, controls)  
  pheno[["final"]] = final
  
  return(pheno)
})

# Create final df
final_df = lapply(data_list, function(pheno){
  out = pheno[["final"]]
  
  return(out)
}) %>% 
  dplyr::bind_rows()

# Set factors
final_df$PHENO <- factor(final_df$PHENO, levels = trait_levels_verb)
final_df$HIT_CONTROL = factor(final_df$HIT_CONTROL, levels = hit_control_levels)

# Create DF for plotting
final_plt = final_df %>% 
  dplyr::select(SNP, PHENO, POPN, RISK_AF, HIT_CONTROL) %>% 
  tidyr::pivot_wider(names_from = POPN, values_from = RISK_AF) 

Analysis

Manhattan plots

Allele frequency distributions of risk alleles

final_df %>%
  dplyr::filter(HIT_CONTROL == "hit") %>% 
  ggplot(aes(RISK_AF, fill = PHENO)) +
    geom_histogram(bins = 100) +
    scale_fill_manual(values = pal_primary) +
    facet_wrap(vars(PHENO), nrow = 2) +
    guides(fill = F) +
    xlab("Risk allele frequency") +
    ylab("Count") +
    theme_bw() +
    ggtitle("Frequency distribution of \"risk\" alleles (all 1KG populations combined)")
ggsave(here("plots", "20210127_af_distribution", "20210127_hits_all.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)

Allele frequency vs effect size

one = final_df %>% 
  dplyr::filter(HIT_CONTROL == "hit" & POPN == "all") %>% 
  ggplot() +
    geom_point(aes(RISK_AF, OR_OR_BETA, colour = PHENO),
               alpha = 0.2) +
    scale_colour_manual(values = pal_primary) +
    facet_wrap(vars(PHENO), nrow = 2) +
    guides(colour = F, alpha = F) +
    theme_bw() +
    ggtitle("Risk allele frequency vs effect size (OR or beta)")

# Zoom in
two = final_df %>% 
  dplyr::filter(HIT_CONTROL == "hit" & POPN == "all") %>% 
  ggplot() +
    geom_point(aes(RISK_AF, OR_OR_BETA, colour = PHENO),
               alpha = 0.2) +
    scale_colour_manual(values = pal_primary) +
    facet_wrap(vars(PHENO), nrow = 2) +
    guides(colour = F, alpha = F) +
    theme_bw() +
    ggtitle("Risk allele frequency vs effect size (OR or beta)") +
    ylim(0,20)

one
two
ggsave(here("plots", "20210127_af_v_effect_size", "20210127_hits_all.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)
ggsave(here("plots", "20210127_af_v_effect_size", "20210127_hits_zoomed.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)

Fst

With all populations

Get Fst stats for top SNPs

# Create raw list of variants
hit = here::here("data", "20210128_hits_vcfs")
control = here::here("data", "20210128_rndm_vcfs")
target_dirs = c(hit, control)
names(target_dirs) = c("hit", "control")

# Set seed
#initial_seed = 53
#set.seed(initial_seed)
#seeds = sample(1:1000, size = length(target_dirs))

# Run 
counter = 0
fst_out = lapply(target_dirs, function(target_dir){
  # set counter 
  counter <<- counter + 1
  
  vcf_list_raw <- lapply(trait_levels, function(trait){
    target_file = file.path(target_dir, paste(trait, ".vcf.gz", sep = ""))
    # read in file
    vcf_out <- pegas::read.vcf(target_file)
    
    return(vcf_out)
  })
  
  # Create vector of populations
  populations <- unlist(lapply(rownames(vcf_list_raw[[1]]), function(sample){
    meta$Population[meta$Sample == sample]
  }))
  
  # Generate Fst stats
  fst_out_df <- lapply(vcf_list_raw, function(pheno){
    out = as.data.frame(pegas::Fst(pheno, pop = populations))
    # put rownames into separate column
    out$snp <- rownames(out)
    
    return(out)
  }) %>% 
    # bind into single DF
    dplyr::bind_rows(.id = "phenotype") %>% 
    # remove NA
    tidyr::drop_na()
  
  return(fst_out_df)
}) %>% 
  dplyr::bind_rows(.id = "hit_control")

# Recode phenotype
fst_out$phenotype <- factor(fst_out$phenotype, levels = trait_levels)
fst_out$phenotype = dplyr::recode(fst_out$phenotype, !!!recode_vec)

Write to file

out_dir = here::here("data", "20210127_results")
out_path = file.path(out_dir, paste("20210128_fst", ".csv", sep = ""))

dir.create(out_dir)

readr::write_csv(fst_out, out_path)

Read back in

fst_out = readr::read_csv(here::here("data", "20210127_results/20210128_fst.csv"))
fst_out$phenotype <- factor(fst_out$phenotype, levels = trait_levels_verb)

knitr::kable(head(fst_out))
hit_control phenotype Fit Fst Fis snp
hit Height 1 0.0543812 1 rs2710889
hit Height 1 0.2898026 1 rs1240697
hit Height 1 0.0972836 1 rs28401288
hit Height 1 0.0669376 1 rs377599
hit Height 1 0.0274605 1 rs12028979
hit Height 1 0.0888646 1 rs5024246

Fst histograms

one = fst_out %>%
  dplyr::filter(hit_control == "hit") %>% 
  ggplot() +
    geom_histogram(aes(Fst, fill = phenotype), bins = 100) +
    facet_wrap(~phenotype) +
    theme_bw() +
    scale_fill_manual(values = pal_primary)  +
    guides(fill = F)

two = fst_out %>%
  dplyr::filter(hit_control == "control") %>% 
  ggplot() +
    geom_histogram(aes(Fst, fill = phenotype), bins = 100) +
    facet_wrap(~phenotype) +
    theme_bw() +
    scale_fill_manual(values = pal_secondary) +
    guides(fill = F)

one
two
ggsave(here("plots", "20210127_histograms", "20210128_hits_all.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)
ggsave(here("plots", "20210127_histograms", "20210128_controls_all.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)

Fst density

Facets
one = fst_out %>% 
  dplyr::filter(hit_control == "hit") %>% 
  ggplot(aes(Fst, fill = phenotype)) +
    geom_density() +
    labs(fill = "Phenotype") +
    facet_wrap(~phenotype) +
    ylab("Density") +
    theme_bw() +
    scale_fill_manual(values = pal_primary) +
    guides(fill = F)


two = fst_out %>% 
  dplyr::filter(hit_control == "control") %>% 
  ggplot(aes(Fst, fill = phenotype)) +
    geom_density() +
    labs(fill = "Phenotype") +
    facet_wrap(~phenotype) +
    ylab("Density") +
    theme_bw() +
    scale_fill_manual(values = pal_secondary) +
    guides(fill = F)

one
two
ggsave(here("plots", "20210127_densities", "20210128_hits_all.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)
ggsave(here("plots", "20210127_densities", "20210128_controls_all.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)
Ridges
one = fst_out %>% 
  dplyr::filter(hit_control == "hit") %>% 
  ggplot() +
    geom_density_ridges2(mapping = aes(x = Fst, y = phenotype, fill = phenotype),
                         scale = 2) +
    scale_fill_manual(values = pal_primary) +
    ylab(label = NULL) +
    theme_bw() +
    guides(fill = F) +
    scale_y_discrete(expand = expand_scale(add = c(0.2, 2.3)))

two = fst_out %>%
  dplyr::filter(hit_control == "control") %>% 
  ggplot() +
    geom_density_ridges2(mapping = aes(x = Fst, y = phenotype, fill = phenotype),
                         scale = 2) +
    scale_fill_manual(values = pal_secondary) +
    ylab(label = NULL) +
    theme_bw() +
    guides(fill = F) +
    scale_y_discrete(expand = expand_scale(add = c(0.2, 2.3)))

one
two
ggsave(here("plots", "20210127_ridges", "20210128_hits_all.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)
ggsave(here("plots", "20210127_ridges", "20210128_controls_all.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)

Run Kolmogorov-Smirnov Tests

hit_control = unique(fst_out$hit_control)
names(hit_control) = hit_control

ks_out = lapply(hit_control, function(dataset){
  # filter dataset
  target_df = fst_out[fst_out$hit_control == dataset, ]
  # run pairwise KS tests
  ks_out = lapply(trait_levels_verb, function(trait_a){
    out = lapply(trait_levels_verb, function(trait_b){
      results = ks.test(target_df$Fst[target_df$phenotype == trait_a],
                        target_df$Fst[target_df$phenotype == trait_b])
      P = results$p.value
      
      return(P)
    }) %>% 
      dplyr::bind_rows(.id = "test_b")
    
    return(out)
  })  %>% 
    dplyr::bind_rows(.id = "trait")
  
  traits = ks_out$trait
  ks_out$trait <- NULL
  
  rownames(ks_out) = traits
  return(ks_out)
})

# convert to matrix
ks_mat = lapply(ks_out, function(dataset){
  out = as.matrix(dataset)
  
  return(out)
})
# Process for plotting
ks_out_gg = lapply(ks_out, function(dataset){
  out = dataset
  out$A = rownames(dataset)
  out = out %>% 
    pivot_longer(cols = -A, names_to = "B", values_to = "ks_P")
  # convert P-values to -log10
  out$ks_P = -log10(out$ks_P)
  
  return(out)
}) %>% 
  dplyr::bind_rows(.id = "hit_control") %>% 
  dplyr::mutate(across(c("A", "B"),
                       ~factor(.x, levels = trait_levels_verb)))

Plot

heat_hits_all = ks_out_gg %>% 
  dplyr::filter(hit_control == "hit") %>% 
  ggplot() +
    geom_tile(aes(A, B, fill = ks_P)) +
    scale_fill_viridis_c() +
    coord_fixed() +
    xlab(NULL) +
    ylab(NULL) +
    labs(fill = "KS-test\n-log(P)")

heat_controls_all = ks_out_gg %>% 
  dplyr::filter(hit_control == "control") %>% 
  ggplot() +
    geom_tile(aes(A, B, fill = ks_P)) +
    scale_fill_viridis_c(option = "magma") +
    coord_fixed() +
    xlab(NULL) +
    ylab(NULL) +
    labs(fill = "KS-test\n-log(P)")

heat_hits_all
heat_controls_all
ggsave(here("plots", "20210127_ks_heatmaps", "20210127_hits_all.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)
ggsave(here("plots", "20210127_ks_heatmaps", "20210127_controls_all.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)

Try with same number of SNPs

Strategy: Will it make a difference if we analyse the same number of SNPs for each trait? i.e. is the different number of SNPs for each trait affecting the KS test output?

IBD only has 182 hits, so remove from this part of the analysis.

The trait with the next least number of hits is Intelligence, with `480. So we’ll take a random sample of 40

fst_sample = split(fst_out, f = fst_out$hit_control)
fst_sample = lapply(fst_sample, function(dataset){
  out = split(dataset, f = dataset$phenotype)
  out = lapply(out, function(pheno){
    pheno = pheno %>% 
      dplyr::slice_sample(n = 480)
    
    return(pheno)
  }) %>% 
    dplyr::bind_rows() %>% 
    dplyr::filter(phenotype != "IBD")
}) %>% 
  dplyr::bind_rows()

Histograms

one = fst_sample %>%
  dplyr::filter(hit_control == "hit") %>% 
  ggplot() +
    geom_histogram(aes(Fst, fill = phenotype), bins = 100) +
    facet_wrap(~phenotype) +
    theme_bw() +
    scale_fill_manual(values = pal_primary)  +
    guides(fill = F)

two = fst_sample %>%
  dplyr::filter(hit_control == "control") %>% 
  ggplot() +
    geom_histogram(aes(Fst, fill = phenotype), bins = 100) +
    facet_wrap(~phenotype) +
    theme_bw() +
    scale_fill_manual(values = pal_secondary) +
    guides(fill = F)

one
two
ggsave(here("plots", "20210127_histograms", "20210127_hits_sample.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)
ggsave(here("plots", "20210127_histograms", "20210127_controls_sample.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)

Density

one = fst_sample %>% 
  dplyr::filter(hit_control == "hit") %>% 
  ggplot(aes(Fst, fill = phenotype)) +
    geom_density() +
    labs(fill = "Phenotype") +
    facet_wrap(~phenotype) +
    ylab("Density") +
    theme_bw() +
    scale_fill_manual(values = pal_primary) +
    guides(fill = F)

two = fst_sample %>% 
  dplyr::filter(hit_control == "control") %>% 
  ggplot(aes(Fst, fill = phenotype)) +
    geom_density() +
    labs(fill = "Phenotype") +
    facet_wrap(~phenotype) +
    ylab("Density") +
    theme_bw() +
    scale_fill_manual(values = pal_secondary) +
    guides(fill = F)

one
two
ggsave(here("plots", "20210127_densities", "20210127_hits_sample.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)
ggsave(here("plots", "20210127_densities", "20210127_controls_sample.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)

KS test

hit_control = unique(fst_sample$hit_control)
names(hit_control) = hit_control

trait_levels_verb_sample = trait_levels_verb[-which(trait_levels_verb == "IBD")]
traits_sample = traits[-which(traits == "ibd")]

ks_sample = lapply(hit_control, function(dataset){
  # filter dataset
  target_df = fst_sample[fst_sample$hit_control == dataset, ]
  # run pairwise KS tests
  ks_out = lapply(trait_levels_verb_sample, function(trait_a){
    out = lapply(trait_levels_verb_sample, function(trait_b){
      results = ks.test(target_df$Fst[target_df$phenotype == trait_a],
                        target_df$Fst[target_df$phenotype == trait_b])
      P = results$p.value
      
      return(P)
    }) %>% 
      dplyr::bind_rows(.id = "test_b")
    
    return(out)
  })  %>% 
    dplyr::bind_rows(.id = "trait")
  
  traits = ks_out$trait_levels_verb_sample
  ks_out$trait <- NULL
  
  rownames(ks_out) = trait_levels_verb_sample
  return(ks_out)
})

Heatmaps

ks_sample_gg = lapply(ks_sample, function(dataset){
  out = dataset
  out$A = rownames(dataset)
  out = out %>% 
    pivot_longer(cols = -A, names_to = "B", values_to = "ks_P")
  # convert P-values to -log10
  out$ks_P = -log10(out$ks_P)
  
  return(out)
}) %>% 
  dplyr::bind_rows(.id = "hit_control") %>% 
  dplyr::mutate(across(c("A", "B"),
                       ~factor(.x, levels = trait_levels_verb_sample)))

Plot

heat_hits_sample = ks_sample_gg %>% 
  dplyr::filter(hit_control == "hit") %>% 
  ggplot() +
    geom_tile(aes(A, B, fill = ks_P)) +
    scale_fill_viridis_c() +
    coord_fixed() +
    xlab(NULL) +
    ylab(NULL) +
    labs(fill = "KS-test\n-log(P)")

heat_controls_sample = ks_sample_gg %>% 
  dplyr::filter(hit_control == "control") %>% 
  ggplot() +
    geom_tile(aes(A, B, fill = ks_P)) +
    scale_fill_viridis_c(option = "magma") +
    coord_fixed() +
    xlab(NULL) +
    ylab(NULL) +
    labs(fill = "KS-test\n-log(P)")
   
heat_hits_sample
heat_controls_sample
ggsave(here("plots", "20210127_ks_heatmaps", "20210127_hits_sample.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)
ggsave(here("plots", "20210127_ks_heatmaps", "20210127_controls_sample.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)

Compare effects of sampling on P-values

heat_hits_all
heat_hits_sample
heat_controls_all
heat_controls_sample

Try different ways of testing differences

Read in Fst results

fst_out = readr::read_csv(here::here("data", "20210127_results/20210128_fst.csv"))
fst_out$phenotype <- factor(fst_out$phenotype, levels = trait_levels_verb[-1])

Run Kolmogorov-Smirnov Tests

From the help pages of ks.test:

The possible values “two.sided”, “less” and “greater” of alternative specify the null hypothesis that the true distribution function of x is equal to, not less than or not greater than the hypothesized distribution function (one-sample case) or the distribution function of y (two-sample case), respectively. This is a comparison of cumulative distribution functions, and the test statistic is the maximum difference in value, with the statistic in the "greater" alternative being D^+ = max[F_x(u) - F_y(u)]. Thus in the two-sample case alternative = "greater" includes distributions for which x is stochastically smaller than y (the CDF of x lies above and hence to the left of that for y), in contrast to t.test or wilcox.test.

ks_out = split(fst_out, f = fst_out$hit_control)
ks_out = lapply(ks_out, function(dataset){
  split_data = split(dataset, f = dataset$phenotype)
  # run pairwise KS tests
  ks_out = lapply(split_data, function(trait_a){
    out = lapply(split_data, function(trait_b){
      # "two.sided"
      results_two_sided = ks.test(trait_a$Fst,
                                  trait_b$Fst,
                                  alternative = "two.sided")      
      # "less" test
      results_less = ks.test(trait_a$Fst,
                             trait_b$Fst,
                             alternative = "less")
      # "greater" test
      results_greater = ks.test(trait_a$Fst,
                                trait_b$Fst,
                                alternative = "greater")
      # Mann-Whitney 
      results_mw = wilcox.test(trait_a$Fst,
                               trait_b$Fst,
                               alternative = "two.sided")
      # bind into DF
      df_out = c(results_two_sided[["statistic"]],
                 "P_TWO_SIDED" = results_two_sided[["p.value"]],
                 results_less[["statistic"]],
                 "P_LESS" = results_less[["p.value"]],
                 results_greater[["statistic"]],
                 "P_GREATER" = results_greater[["p.value"]],
                 results_mw[["statistic"]],
                 "P_MW" = results_mw[["p.value"]])

      return(df_out)
      return(results_mw)
    }) %>% 
      dplyr::bind_rows(.id = "B")
  }) %>% 
    dplyr::bind_rows(.id = "A")
}) %>% 
  dplyr::bind_rows(.id = "HIT_CONTROL") %>% 
  dplyr::mutate(across(c("A", "B"),
                       ~factor(.x, levels = trait_levels_verb)))

Plot KS-test D

Show correlation

plt = ks_out %>% 
  ggplot() +
    geom_point(aes(`D^+`, `D^-`, colour = HIT_CONTROL,
                   text = paste("Trait A: ", A,
                                "<br>Trait B: ", B))) +
    theme_bw() +
    coord_fixed()
Ignoring unknown aesthetics: text
ggplotly(plt)

NA
ggplotly(plt) %>%
  export(file = here::here("plots", "20210129_KS-D.svg"),
         selenium = RSelenium::rsDriver(browser = "firefox"))

Plot MW

heat_hits_sample = ks_out %>% 
  dplyr::filter(CASE_CONTROL == "case") %>% 
  ggplot() +
    geom_tile(aes(A, B, fill = -log10(P_MW))) +
    scale_fill_viridis_c() +
    coord_fixed() +
    xlab(NULL) +
    ylab(NULL) +
    labs(fill = "MW-test\n-log(W)")

heat_controls_sample = ks_out %>% 
  dplyr::filter(CASE_CONTROL == "control") %>% 
  ggplot() +
    geom_tile(aes(A, B, fill = -log10(P_MW))) +
    scale_fill_viridis_c(option = "magma") +
    coord_fixed() +
    xlab(NULL) +
    ylab(NULL) +
    labs(fill = "MW-test\n-log(W)")
   
heat_hits_sample

heat_controls_sample

Calculate Fst for 100k random SNPs to compare to these distributions

Pull random lines from 1kg alfreq file

# On cluster 

list_in=../big_data/20210125_alfreqs_all_binned/all.afreq
list_out=data/20210129_random_100k_snps.list
ref=../refs/hs37d5.fa.gz
in_vcf=../vcfs/1kg_all.vcf.gz
out_vcf=../vcfs/1kg_100k_rndm.vcf.gz

conda activate fst_env_rhel 

# create function to get random seed
get_seeded_random()
{
  seed="$1"
  openssl enc -aes-256-ctr -pass pass:"$seed" -nosalt \
    </dev/zero 2>/dev/null
}

# make list
awk '{print $2}' $list_in |\
  tail -n+2 |\
  shuf -n 100000 \
  --random-source=<(get_seeded_random 454) \
    > $list_out
    
# extract from 1KG
gatk SelectVariants \
  -R $ref \
  -V $in_vcf \
  --keep-ids $list_out \
  -O $out_vcf

Run pegas to get Fst

# On cluster
library(here)
source(here::here("code", "scripts", "source.R"))

# Set variables
in_vcf=here::here("..", "vcfs", "1kg_100k_rndm.vcf.gz")
samples_file = here::here("data", "20130606_sample_info.xlsx")
out_file=here::here("data", "20210129_100k_rndm_fst.txt")

# Read in `meta` file
meta = readxl::read_xlsx(samples_file,
                         sheet = "Sample Info") %>%
  dplyr::select(Sample, Population, Gender)

# Read VCF 
vcf_out <- pegas::read.vcf(in_vcf, to = 100000)

# Create vector of populations
populations = unlist(lapply(rownames(vcf_out), function(sample){
  meta$Population[meta$Sample == sample]
}))

# Generate Fst stats
fst_out <- as.data.frame(pegas::Fst(vcf_out, pop = populations))
fst_out$snp <- rownames(fst_out)
# remove NAs
fst_out  = fst_out %>% 
  tidyr::drop_na() 
# 99682 remaining
  
# Set phenotype
fst_out$phenotype <- "Random"

# Save file
readr::write_tsv(fst_out, out_file)

Read in

fst_random = readr::read_tsv(here::here("data", "20210129_100k_rndm_fst.txt")) %>% 
  dplyr::mutate(hit_control = "hit")

── Column specification ──────────────────────────────────────────────────────────────────────
cols(
  Fit = col_double(),
  Fst = col_double(),
  Fis = col_double(),
  snp = col_character(),
  phenotype = col_character()
)

Plot

Fst density

Facets
fst_out %>% 
  dplyr::filter(hit_control == "hit") %>% 
  ggplot(aes(Fst, fill = phenotype)) +
    geom_density() +
    labs(fill = "Phenotype") +
    facet_wrap(~phenotype) +
    ylab("Density") +
    theme_bw() +
    scale_fill_manual(values = pal_primary) +
    guides(fill = F)

Ridges

Final

Get median Fst and 0.9 percentile

Read in data

# Read in data
fst_out = readr::read_csv(here::here("data", "20210127_results/20210128_fst.csv"))

── Column specification ───────────────────────────────────────────────────────────
cols(
  hit_control = col_character(),
  phenotype = col_character(),
  Fit = col_double(),
  Fst = col_double(),
  Fis = col_double(),
  snp = col_character()
)
fst_out$phenotype <- factor(fst_out$phenotype, levels = trait_levels_verb)

Median and 0.9 percentile

From the help page of wilcoxon.test:

Note that in the two-sample case the estimator for the difference in location parameters does not estimate the difference in medians (a common misconception) but rather the median of the difference between a sample from x and a sample from y.

ks.test(fst_list$hit$Height$Fst, fst_list$hit$`Educational attainment`$Fst)
p-value will be approximate in the presence of ties

    Two-sample Kolmogorov-Smirnov test

data:  fst_list$hit$Height$Fst and fst_list$hit$`Educational attainment`$Fst
D = 0.087524, p-value = 1.461e-06
alternative hypothesis: two-sided

Plot

mw_gg %>% 
  ggplot() +
    geom_col(aes(PHENO, -log10(P_MW), fill = PHENO)) +
    facet_wrap(~HIT_CONTROL) + 
    scale_fill_manual(values = pal_primary) +
    theme_bw() +
    ggtitle("Mann-Whitney p-values")

mw_gg %>% 
  ggplot() +
    geom_col(aes(PHENO, -log10(P_KS), fill = PHENO)) +
    facet_wrap(~HIT_CONTROL) + 
    scale_fill_manual(values = pal_primary) +
    theme_bw() +
    ggtitle("KS-test p-values")

LS0tCnRpdGxlOiAiRnN0IHZhcmlhdGlvbiBhY3Jvc3MgaHVtYW4gdHJhaXRzIgphdXRob3I6ICJJYW4gQnJldHRlbGwiCmRhdGU6ICdgciBmb3JtYXQoU3lzLkRhdGUoKSlgJwplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKI291dHB1dDoKIyAgaHRtbF9kb2N1bWVudDoKIyAgICB0b2M6IHRydWUKIyAgICB0b2NfZmxvYXQ6IHRydWUKIyAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKIyAgICBrZWVwX21kOiB0cnVlCiMgICAgcGFuZG9jX2FyZ3M6IC0tbHVhLWZpbHRlcj1jb2xvci10ZXh0Lmx1YQojICAgIGhpZ2hsaWdodDogcHlnbWVudHMgIAotLS0KCiMgU2V0dXAKCiogW1dvcmtpbmcgZGlyZWN0b3J5XXtjb2xvcj0iIzRmMDk0MyJ9IG9uIEVCSSBDbHVzdGVyOiBgL2hwcy9yZXNlYXJjaDEvYmlybmV5L3VzZXJzL2lhbi9obW5fZnN0YAoqIFtHaXRIdWIgcmVwb3NpdG9yeV17Y29sb3I9IiM0ZjA5NDMifTogPGh0dHBzOi8vZ2l0aHViLmNvbS9icmV0dGVsbGViaS9odW1hbl90cmFpdHNfZnN0PgoKIyMgYGNvbmRhYCBlbnYgb24gY2x1c3RlcgoKYGBge3IsIGVuZ2luZT0nYmFzaCcsIGV2YWwgPSBGfQojIENyZWF0ZSBlbnYgb24gY2x1c3RlciB3aXRoIG1hbWJhCm1hbWJhIGNyZWF0ZSAteSBcCiAgLW4gZnN0X2Vudl9yaGVsIFwKICAtYyBiaW9jb25kYSBnYXRrNApjb25kYSBhY3RpdmF0ZSBmc3RfZW52X3JoZWwKbWFtYmEgaW5zdGFsbCBiY2Z0b29scyBwbGluazIgci1iYXNlIHItZXNzZW50aWFscyByLXRpZHl2ZXJzZSByLXVuaXRzIGxpYmdkYWwgci1zZgojIEV4cG9ydApjb25kYSBlbnYgZXhwb3J0IFwKICAtLW5vLWJ1aWxkcyBcCiAgLWYgZW52cy9mc3RfZW52X3JoZWwueW1sCiMgQWN0aXZhdGUKY29uZGEgYWN0aXZhdGUgZnN0X2Vudl9yaGVsCmBgYAoKIyMgYHJlbnZgCgpgYGB7ciwgZXZhbCA9IEZ9CiMgRXhwb3J0IGVudiAodG8gcmVudi5sb2NrIGZpbGUpCnJlbnY6OmluaXQoKQojIFRvIGluc3RhbGwgcGFja2FnZXMgb24gbmV3IHN5c3RlbSwgb3IgJ2FjdGl2YXRlJyB0aGUgZW52OiAKcmVudjo6cmVzdG9yZSgpCmBgYAoKIyMgU291cmNlIGxpYnJhcmllcywgZnVuY3Rpb25zIGFuZCBwbG90dGluZyBwYXJhbWV0ZXJzCgpgYGB7ciwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQpsaWJyYXJ5KGhlcmUpCgpzb3VyY2UoaGVyZTo6aGVyZSgiY29kZSIsICJzY3JpcHRzIiwgInNvdXJjZS5SIikpCmBgYAoKIyMgRG93bmxvYWQgMUtHIGRhdGEKCiMjIyBEb3dubG9hZCBmcm9tIEZUUAoKYGBge2Jhc2gsIGV2YWwgPSBGfQp3Z2V0IFwKICAtciAtcCAtayBcCiAgLS1uby1wYXJlbnQgXAogIC1jdXQtZGlycz01IFwKICBmdHA6Ly9mdHAuMTAwMGdlbm9tZXMuZWJpLmFjLnVrL3ZvbDEvZnRwL3JlbGVhc2UvMjAxMzA1MDIvCmBgYAoKIyMjIFB1dCBmaWxlbmFtZXMgaW50byBsaXN0CgpgYGB7ciwgZW5naW5lPSdiYXNoJywgZXZhbCA9IEZ9CmZpbmQgdmNmcy9mdHAuMTAwMGdlbm9tZXMuZWJpLmFjLnVrL0FMTC5jaHIqLnZjZi5neiBcCiAgPiBodW1hbl90cmFpdHNfZnN0L2RhdGEvMjAyMDAyMDVfdmNmcy5saXN0CmBgYAoKIyMjIE1lcmdlIFZDRnMKCmBgYHtyLCBlbmdpbmU9J2Jhc2gnLCBldmFsID0gRn0KamF2YSAtamFyIC9uZnMvc29mdHdhcmUvYmlybmV5L3BpY2FyZC0yLjkuMC9waWNhcmQuamFyIE1lcmdlVmNmcyBcCiAgST1odW1hbl90cmFpdHNfZnN0L2RhdGEvMjAyMDAyMDVfdmNmcy5saXN0IFwKICBPPXZjZnMvMWtnX2FsbC52Y2YuZ3oKIyBFeGNlcHRpb24gaW4gdGhyZWFkICJtYWluIiBqYXZhLmxhbmcuSWxsZWdhbEFyZ3VtZW50RXhjZXB0aW9uOiBUaGUgY29udGlnIGVudHJpZXMgaW4gaW5wdXQgZmlsZSAvaHBzL3Jlc2VhcmNoMS9iaXJuZXkvdXNlcnMvaWFuL3JhY19oeXAvdmNmcy9mdHAuMTAwMGdlbm9tZXMuZWJpLmFjLnVrL0FMTC5jaHJNVC5waGFzZTNfY2FsbG1vbS12MF80LjIwMTMwNTAyLmdlbm90eXBlcy52Y2YuZ3ogYXJlIG5vdCBjb21wYXRpYmxlIHdpdGggdGhlIG90aGVycy4KCiMgU28gcmVtb3ZlIHRoYXQgb25lIGZyb20gbGlzdCBhYm92ZQpzZWQgLWkgJy9NVC9kJyBodW1hbl90cmFpdHNfZnN0L2RhdGEvMjAyMDAyMDVfdmNmcy5saXN0CgojIHJ1biBNZXJnZVZDRnMgYWdhaW4KamF2YSAtamFyIC9uZnMvc29mdHdhcmUvYmlybmV5L3BpY2FyZC0yLjkuMC9waWNhcmQuamFyIE1lcmdlVmNmcyBcCiAgST1odW1hbl90cmFpdHNfZnN0L2RhdGEvMjAyMDAyMDVfdmNmcy5saXN0IFwKICBPPXZjZnMvMWtnX2FsbC52Y2YuZ3oKICAKIyBFeGNlcHRpb24gaW4gdGhyZWFkICJtYWluIiBqYXZhLmxhbmcuSWxsZWdhbEFyZ3VtZW50RXhjZXB0aW9uOiBUaGUgY29udGlnIGVudHJpZXMgaW4gaW5wdXQgZmlsZSAvaHBzL3Jlc2VhcmNoMS9iaXJuZXkvdXNlcnMvaWFuL3JhY19oeXAvdmNmcy9mdHAuMTAwMGdlbm9tZXMuZWJpLmFjLnVrL0FMTC5jaHJZLnBoYXNlM19pbnRlZ3JhdGVkX3YyYS4yMDEzMDUwMi5nZW5vdHlwZXMudmNmLmd6IGFyZSBub3QgY29tcGF0aWJsZSB3aXRoIHRoZSBvdGhlcnMuCnNlZCAtaSAnL2NoclkvZCcgaHVtYW5fdHJhaXRzX2ZzdC9kYXRhLzIwMjAwMjA1X3ZjZnMubGlzdAoKIyBydW4gTWVyZ2VWQ0ZzIGFnYWluCmphdmEgLWphciAvbmZzL3NvZnR3YXJlL2Jpcm5leS9waWNhcmQtMi45LjAvcGljYXJkLmphciBNZXJnZVZjZnMgXAogIEk9aHVtYW5fdHJhaXRzX2ZzdC9kYXRhLzIwMjAwMjA1X3ZjZnMubGlzdCBcCiAgTz12Y2ZzLzFrZ19hbGwudmNmLmd6CiMgU1VDQ0VTUwpgYGAKCiMjIE9idGFpbiBHV0FTIGRhdGEgZnJvbSB0aGUgR1dBUyBDYXRhbG9nIDxodHRwczovL3d3dy5lYmkuYWMudWsvZ3dhcz4KCiMjIyBQdWxsIGRhdGEgZm9yIGVhY2ggdHJhaXQKClsqKk5PVEUqKl17Y29sb3I9InJlZCJ9OiBVbmNoZWNrIGBJbmNsdWRlIGNoaWxkIHRyYWl0IGRhdGFgIGJlZm9yZSBkb3dubG9hZGluZy4KCkFsbCBkb2N1bWVudHMgZG93bmxvYWRlZCB2aWEgJ0Rvd25sb2FkIENhdGFsb2cgZGF0YScgbGluaywgdGhlbiBjb2xsYXRlZCBhbmQgc2F2ZWQgaGVyZTogYGRhdGEvMjAyMTAxMjJfZ3dhc19jYXRhbG9nLnhsc3hgCgojIyMjIEhlaWdodAoKKiBoZWlnaHQ6IDxodHRwczovL3d3dy5lYmkuYWMudWsvZ3dhcy9lZm90cmFpdHMvRUZPXzAwMDQzMzk+CiAgLSAqKjQ5MTIgU05QcyoqIGZyb20gKio1MSBzdHVkaWVzKioKICAKIyMjIyBCTUkKCiogYm1pOiA8aHR0cHM6Ly93d3cuZWJpLmFjLnVrL2d3YXMvZWZvdHJhaXRzL0VGT18wMDA0MzQwPgogIC0gKio3NTczIFNOUHMqKiBmcm9tICoqMTU1IHN0dWRpZXMqKgogIAojIyMjIEVkdWNhdGlvbmFsIGF0dGFpbm1lbnQKCiogc2VsZiByZXBvcnRlZCBlZHVjYXRpb25hbCBhdHRhaW5tZW50OiA8aHR0cHM6Ly93d3cuZWJpLmFjLnVrL2d3YXMvZWZvdHJhaXRzL0VGT18wMDA0Nzg0PgogIC0gKiozOTg5IFNOUHMqKiBmcm9tICoqMjQgc3R1ZGllcyoqCiAgCiMjIyMgSW50ZWxsaWdlbmNlCgoqIGludGVsbGlnZW5jZTogPGh0dHBzOi8vd3d3LmViaS5hYy51ay9nd2FzL2Vmb3RyYWl0cy9FRk9fMDAwNDMzNz4KICAtICoqMjk2NyBTTlBzKiogZnJvbSAqKjI3IHN0dWRpZXMqKgogIAojIyMjIElCRAoKKiBpbmZsYW1tYXRvcnkgYm93ZWwgZGlzZWFzZTogPGh0dHBzOi8vd3d3LmViaS5hYy51ay9nd2FzL2Vmb3RyYWl0cy9FRk9fMDAwMzc2Nz4KICAtICoqNTM2IFNOUHMqKiBmcm9tICoqMzQgc3R1ZGllcyoqCgojIyMjIFBpZ21lbnRhdGlvbgoKKiBza2luIHBpZ21lbnRhdGlvbjogPGh0dHBzOi8vd3d3LmViaS5hYy51ay9nd2FzL2Vmb3RyYWl0cy9FRk9fMDAwMzc4ND4KICAtICoqMTAyIFNOUHMqKiBmcm9tICoqNiBzdHVkaWVzKioKCiogc2tpbiBwaWdtZW50YXRpb24gbWVhc3VyZW1lbnQ6IDxodHRwczovL3d3dy5lYmkuYWMudWsvZ3dhcy9lZm90cmFpdHMvRUZPXzAwMDcwMDk+CiAgLSAqKjIzMyBTTlBzKiogZnJvbSAqKjkgc3R1ZGllcyoqCgoqIGV5ZSBjb2xvcjogPGh0dHBzOi8vd3d3LmViaS5hYy51ay9nd2FzL2Vmb3RyYWl0cy9FRk9fMDAwMzk0OT4KICAtICoqNzcgU05QcyoqIGZyb20gKioxMyBzdHVkaWVzKioKICAKKiBleWUgY29sb3VyIG1lYXN1cmVtZW50Ogo8aHR0cHM6Ly93d3cuZWJpLmFjLnVrL2d3YXMvZWZvdHJhaXRzL0VGT18wMDA5NzY0PgogIC0gKioyMDIgU05QcyoqIGZyb20gKio5IHN0dWRpZXMqKgogIAoqIGhhaXIgY29sb3I6IDxodHRwczovL3d3dy5lYmkuYWMudWsvZ3dhcy9lZm90cmFpdHMvRUZPXzAwMDM5MjQ+CiAgLSAqKjQyNCBTTlBzKiogZnJvbSAqKjE4IHN0dWRpZXMqKgogIAoqIGhhaXIgY29sb3VyIG1lYXN1cmVtZW50OiA8aHR0cHM6Ly93d3cuZWJpLmFjLnVrL2d3YXMvZWZvdHJhaXRzL0VGT18wMDA3ODIyPgogIC0gKio1NDEgU05QcyoqIGZyb20gKio2IHN0dWRpZXMqKgoKIyMjIFJlYWQgaW50byBsaXN0CgpgYGB7ciwgd2FybmluZ3MgPSBGfQpmaWxlX2luID0gaGVyZTo6aGVyZSgiZGF0YSIsICIyMDIxMDEyMl9nd2FzX2NhdGFsb2cueGxzeCIpCiMgQ3JlYXRlIHZlY3RvciBvZiB0cmFpdHMKdHJhaXRzID0gYygiaGVpIiwgImJtaSIsICJlZHUiLCAiaW50IiwgImliZCIsICJwaWciKQpuYW1lcyh0cmFpdHMpID0gdHJhaXRzCiMgQXNzaWduIHNoZWV0cyB0byB0cmFpdHMKc2hlZXRzIDwtIHNlcSgxOjExKQpuYW1lcyhzaGVldHMpIDwtIGMoImhlaSIsICJibWkiLCAiZWR1IiwgImludCIsICJpYmQiLCByZXAoInBpZyIsIDYpKQoKIyBnZXQgc2hlZXRzCnNoZWV0X25hbWVzIDwtIHJlYWR4bDo6ZXhjZWxfc2hlZXRzKGZpbGVfaW4pCgojIENyZWF0ZSBmdW5jdGlvbiB0byByZWFkIGluIGRhdGEKcmVhZF9jYXRhbG9nX2RhdGEgPC0gZnVuY3Rpb24ocGF0aCwgdGFyZ2V0X3NoZWV0KXsKICAjIFJlYWQgaW4gZGF0YQogIG91dCA9IHJlYWR4bDo6cmVhZF94bHN4KHBhdGgsIHNoZWV0ID0gdGFyZ2V0X3NoZWV0KSAlPiUgCiAgICBkcGx5cjo6c2VsZWN0KENIUiA9IENIUl9JRCwgCiAgICAgICAgICAgICAgICAgIFBPUyA9IENIUl9QT1MsIAogICAgICAgICAgICAgICAgICBTTlBfQUwgPSBgU1RST05HRVNUIFNOUC1SSVNLIEFMTEVMRWAsIAogICAgICAgICAgICAgICAgICBQID0gYFAtVkFMVUVgLCAKICAgICAgICAgICAgICAgICAgT1JfT1JfQkVUQSA9IGBPUiBvciBCRVRBYCwgCiAgICAgICAgICAgICAgICAgIE1BUFBFRF9UUkFJVCwKICAgICAgICAgICAgICAgICAgU1RVRFkgPSBgU1RVRFkgQUNDRVNTSU9OYCwKICAgICAgICAgICAgICAgICAgU0FNUExFID0gYElOSVRJQUwgU0FNUExFIFNJWkVgKSAlPiUgCiAgICAjIFNwbGl0IFNOUCBhbmQgcmlzayBhbGxlbGUgaW50byBzZXBhcmF0ZSBjb2x1bW5zCiAgICBkcGx5cjo6bXV0YXRlKFRPUF9TTlAgPSBzdHJpbmdyOjpzdHJfc3BsaXQoU05QX0FMLCAiLSIsIHNpbXBsaWZ5ID0gVClbLCAxXSwKICAgICAgICAgICAgICAgICAgUklTS19BTExFTEUgPSBzdHJpbmdyOjpzdHJfc3BsaXQoU05QX0FMLCAiLSIsIHNpbXBsaWZ5ID0gVClbLCAyXSkgJT4lIAogICAgIyBSZW9yZGVyIGFuZCBzZWxlY3QKICAgIGRwbHlyOjpzZWxlY3QoQ0hSLCBQT1MsIFRPUF9TTlAsIFJJU0tfQUxMRUxFLCBQLCBPUl9PUl9CRVRBLCBNQVBQRURfVFJBSVQsIFNUVURZLCBTQU1QTEUpCiAgIyBDaGFuZ2UgdmFyaWFibGVzIHRvIHNwZWNpZmljIHR5cGVzCiAgb3V0JENIUiA8LSBhcy5pbnRlZ2VyKG91dCRDSFIpCiAgb3V0JFBPUyA8LSBhcy5udW1lcmljKG91dCRQT1MpCiAgb3V0JFAgPC0gYXMubnVtZXJpYyhvdXQkUCkKICAjIHJldHVybiBERgogIHJldHVybihvdXQpCn0KCiMgUmVhZCBpbiBkYXRhCmNvdW50ZXIgPC0gMApkYXRhX2xpc3QgPSBsYXBwbHkodHJhaXRzLCBmdW5jdGlvbih0cmFpdCl7CiAgIyBzZXQgY291bnRlciAKICBjb3VudGVyIDw8LSBjb3VudGVyICsgMQogICMgc2V0IHRhcmdldCBmaWxlCiAgdGFyZ2V0X2ZpbGUgPSBmaWxlX2luCiAgIyBnZXQgdGFyZ2V0IHNoZWV0CiAgdGFyZ2V0X3NoZWV0ID0gc2hlZXRzW25hbWVzKHNoZWV0cykgPT0gdHJhaXRdCiAgbGVuZ3RoKHRhcmdldF9zaGVldCkKICAjIHJlYWQgaW4gcGlnbWVudGF0aW9uIGRhdGEgZnJvbSBtdWx0aXBsZSBzaGVldHMgYW5kIGJpbmQgaW50byBzaW5nbGUgREYKICBpZiAobGVuZ3RoKHRhcmdldF9zaGVldCkgPiAxKXsKICAgICMgbG9vcCBvdmVyIGVhY2ggc2hlZXQKICAgIGRmIDwtIGxhcHBseSh0YXJnZXRfc2hlZXQsIGZ1bmN0aW9uKHNoZWV0KXsKICAgICAgb3V0IDwtIHJlYWRfY2F0YWxvZ19kYXRhKHRhcmdldF9maWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGFyZ2V0X3NoZWV0ID0gc2hlZXQpCiAgICB9KQogICAgIyBzZXQgbmFtZSBvZiBlYWNoIERGIHRvIG5hbWUgb2Ygc2hlZXQgKHJlcGxhY2luZyBzcGFjZXMgd2l0aCB1bmRlcnNjb3JlcykKICAgIG5hbWVzKGRmKSA9IHNoZWV0X25hbWVzW3RhcmdldF9zaGVldF0gJT4lIAogICAgICBzdHJpbmdyOjpzdHJfcmVwbGFjZV9hbGwoIiAiLCAiXyIpCiAgICAjIGJpbmQgREZzIGludG8gc2luZ2xlIERGCiAgICBkZiA8LSBkcGx5cjo6YmluZF9yb3dzKGRmLCAuaWQgPSAiUElHX1BIRU5PIikKICB9IAogIGVsc2UgewogICAgIyByZWFkIGluIG90aGVyIGRhdGEKICAgIGRmIDwtIHJlYWRfY2F0YWxvZ19kYXRhKHRhcmdldF9maWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgdGFyZ2V0X3NoZWV0ID0gdGFyZ2V0X3NoZWV0KQogIH0KICAjIFNldCBQSEVOTyBjb2x1bW4KICBkZiRQSEVOTyA8LSBmYWN0b3IodHJhaXQsIGxldmVscyA9IHRyYWl0X2xldmVscykKICAjIFJlY29kZSBQSEVOTwogIGRmJFBIRU5PID0gZHBseXI6OnJlY29kZShkZiRQSEVOTywgISEhcmVjb2RlX3ZlYykKICAjIENyZWF0ZSBsaXN0CiAgb3V0ID0gbGlzdCgpCiAgIyBSZXR1cm4gREYgYXMgInJhdyIKICBvdXRbWyJyYXciXV0gPSBkZgogIAogIHJldHVybihvdXQpCn0pCgojIEhvdyBtYW55IFNOUHMgaW4gcmF3IGRhdGEKbGFwcGx5KGRhdGFfbGlzdCwgZnVuY3Rpb24oeCkgbnJvdyh4W1sicmF3Il1dKSkKCiMgQ2xlYW4gZGF0YQpkYXRhX2xpc3QgPSBsYXBwbHkoZGF0YV9saXN0LCBmdW5jdGlvbihwaGVubyl7CiAgZGZfY2xlYW4gPSBwaGVub1tbInJhdyJdXQogICMgUmVtb3ZlIHJvd3Mgd2l0aCBwLXZhbHVlIG9mIDAgKG9ubHkgMzIgb2YgdGhlbSwgYXNzb2NpYXRlZCB3aXRoIHN1bnRhbikKICBkZl9jbGVhbiA9IGRmX2NsZWFuW2RmX2NsZWFuJFAgIT0gMCwgXQogICMgUmVtb3ZlIHJvd3Mgd2l0aCBOQSBpbiBDSFIKICBkZl9jbGVhbiA9IGRmX2NsZWFuWyFpcy5uYShkZl9jbGVhbiRDSFIpLCBdCiAgIyBSZW1vdmUgZHVwbGljYXRlcwogICMjIEZpbmQgU05QcyB0aGF0IGFyZSBkdXBsaWNhdGVkCiAgZHVwZXMgPSB1bmlxdWUoZGZfY2xlYW4kVE9QX1NOUFtkdXBsaWNhdGVkKGRmX2NsZWFuJFRPUF9TTlApIHwgZHVwbGljYXRlZChkZl9jbGVhbiRUT1BfU05QLCBmcm9tTGFzdCA9IFQpXSkKICAjIyBTZWxlY3Qgb25seSAxIFNOUCBmcm9tIGVhY2ggc2V0IG9mIGR1cGxpY2F0ZWQgU05QcwogIGR1cGVfZmlsdCA9IGxhcHBseShkdXBlcywgZnVuY3Rpb24oZHVwZSl7CiAgICAjIFRha2UgdGhlIG9uZSB3aXRoIHRoZSBsb3dlc3QgUC12YWx1ZQogICAgbWluX3AgPSBtaW4oZGZfY2xlYW4kUFtkZl9jbGVhbiRUT1BfU05QID09IGR1cGVdKQogICAgb3V0ID0gZGZfY2xlYW5bZGZfY2xlYW4kVE9QX1NOUCA9PSBkdXBlICYgZGZfY2xlYW4kUCA9PSBtaW5fcCwgXQogICAgIyBJZiB0aGVyZSBhcmUgc3RpbGwgZHVwbGljYXRlcyBkdWUgdG8gaGF2aW5nIGVxdWFsIFAsIHRha2UgdGhlIG9uZSB3aXRoIHRoZSBsYXJnZXN0IGVmZmVjdCBzaXplCiAgICBpZiAobnJvdyhvdXQpID4gMSApewogICAgICBvdXQgPSBvdXRbd2hpY2gubWF4KG91dCRPUl9PUl9CRVRBKSwgXQogICAgfQogICAgcmV0dXJuKG91dCkKICB9KQogICMgQmluZCBsaXN0IGludG8gREYKICBkdXBlX2ZpbHQgPSBkcGx5cjo6YmluZF9yb3dzKGR1cGVfZmlsdCkKICAjIEV4dHJhY3Qgbm9uLWR1cGxpY2F0ZWQgcm93cwogIG5vbl9kdXBlID0gZGZfY2xlYW5bIWR1cGxpY2F0ZWQoZGZfY2xlYW4kVE9QX1NOUCkgJiAhZHVwbGljYXRlZChkZl9jbGVhbiRUT1BfU05QLCBmcm9tTGFzdCA9IFQpLCBdCiAgIyBCaW5kIG5vbi1kdXBsaWNhdGVkIHJvd3Mgd2l0aCBmaWx0ZXJlZCBkdXBsaWNhdGVzCiAgZGZfY2xlYW4gPSByYmluZChub25fZHVwZSwgZHVwZV9maWx0KSAKICAjIEFkZCB0byBsaXN0CiAgcGhlbm9bWyJjbGVhbiJdXSA9IGRmX2NsZWFuCiAgCiAgcmV0dXJuKHBoZW5vKQp9KQoKIyBOZXcgU05QIGNvdW50CmxhcHBseShkYXRhX2xpc3QsIGZ1bmN0aW9uKHgpIG5yb3coeFtbImNsZWFuIl1dKSkKYGBgCgpgYGB7ciwgcmVzdWx0cyA9ICdhc2lzJ30KbGFwcGx5KGRhdGFfbGlzdCwgZnVuY3Rpb24ocGhlbm8pewogIGtuaXRyOjprYWJsZShoZWFkKHBoZW5vW1siY2xlYW4iXV0pKQp9KQpgYGAKCkdldCB1bmlxdWUgbWFwcGVkIHRyYWl0cwoKYGBge3J9CmxhcHBseShkYXRhX2xpc3QsIGZ1bmN0aW9uKHBoZW5vKXsKICB1bmlxdWUocGhlbm8kY2xlYW4kTUFQUEVEX1RSQUlUKQp9KQpgYGAKCkFyZSBhbnkgb2YgdGhlIE9SX09SX0JFVEFzIG5lZ2F0aXZlPyAKCmBgYHtyfQphbnkodW5saXN0KGxhcHBseShkYXRhX2xpc3QsIGZ1bmN0aW9uKHBoZW5vKSB7CiAgYW55KHBoZW5vJGNsZWFuJE9SX09SX0JFVEEgPCAwLG5hLnJtID0gVCkKfSkpKQpgYGAKClRoaXMgbXVzdCBtZWFucyB0aGF0IHRoZSBgUklTS19BTExFTEVgIGFsd2F5cyBhZmZlY3RzIHRoZSB0cmFpdCBwb3NpdGl2ZWx5LiAKCkJ1dCBmb3Igd2hhdCBwcm9wb3J0aW9uIG9mIFNOUHMgaXMgdGhlIGBSSVNLX0FMTEVMRWAgbm90IHN0YXRlZD8KCmBgYHtyfQpsYXBwbHkoZGF0YV9saXN0LCBmdW5jdGlvbihwaGVubykgewogIGxlbmd0aCh3aGljaChwaGVubyRjbGVhbiRSSVNLX0FMTEVMRSA9PSAiPyIpKSAvIG5yb3cocGhlbm8kY2xlYW4pCn0pCmBgYAoKWyoqTk9URSoqXXtjb2xvcj0icmVkIn06IEluIGNhc2VzIHdoZXJlIGBSSVNLX0FMTEVMRWAgaXNuJ3QgcHJvdmlkZWQsIHRyZWF0IHRoZSBgQUxUYCBhbGxlbGUgYXMgYFJJU0tfQUxMRUxFYC4KCiMjIyBHZW5lcmF0ZSBNYW5oYXR0YW4gcGxvdHMKClBsb3QKCmBgYHtyLCBtZXNzYWdlID0gRiwgcmVzdWx0cyA9ICJoaWRlIn0KY291bnRlciA9IDAKbGFwcGx5KGRhdGFfbGlzdCwgZnVuY3Rpb24ocGhlbm9fZGYpewogICMgc2V0IGNvdW50ZXIKICBjb3VudGVyIDw8LSBjb3VudGVyICsgMQogIGRmID0gcGhlbm9fZGZbWyJjbGVhbiJdXQogIHRyYWl0ID0gdW5pcXVlKGRmJFBIRU5PKQogICMgR2V0IHRpdGxlCiAgdGl0bGUgPC0gcGFzdGUodHJhaXQsICJcbiIsICJTTlAgY291bnQ6IiwgbnJvdyhkZikpCiAgIyBQbG90CiAgZ2V0X21hbihkZiwgdHJhaXQgPSB0cmFpdCwgdGl0bGUgPSB0aXRsZSwgY2hyID0gIkNIUiIsIGJwID0gIlBPUyIsIHNucCA9ICJUT1BfU05QIiwgcCA9ICJQIikKfSkKYGBgCgpTYXZlCgpgYGB7ciwgZXZhbCA9IEZ9Cm91dHB1dF9kaXIgPSBoZXJlOjpoZXJlKCJwbG90cyIsICIyMDIxMDEyMl9tYW5oYXR0YW5fYWxsX3NucHMiKQoKY291bnRlciA9IDAKbGFwcGx5KGRhdGFfbGlzdCwgZnVuY3Rpb24ocGhlbm9fZGYpewoKICAjIHNldCBjb3VudGVyCiAgY291bnRlciA8PC0gY291bnRlciArIDEKICBkZiA9IHBoZW5vX2RmW1siY2xlYW4iXV0KICB0cmFpdCA9IHVuaXF1ZShkZiRQSEVOTykKICAjIEdldCB0aXRsZQogIHRpdGxlIDwtIHBhc3RlKHRyYWl0LCAiXG4iLCAiU05QIGNvdW50OiIsIG5yb3coZGYpKQogICMgU2V0IGZpbGUgbmFtZSB0byBzYXZlCiAgZmlsZSA9IGZpbGUucGF0aChvdXRwdXRfZGlyLAogICAgICAgICAgICAgICAgICAgcGFzdGUoIm1hbmhhdHRhbl8iLAogICAgICAgICAgICAgICAgICAgICAgICAgbmFtZXMoZGF0YV9saXN0KVtjb3VudGVyXSwKICAgICAgICAgICAgICAgICAgICAgICAgICIuc3ZnIiwKICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICIiKSkKICAjIFNldCB1cCBncmFwaGljcyBkZXZpY2UKICBzdmcoZmlsZSwKICAgICAgd2lkdGggPSAxMCwKICAgICAgaGVpZ2h0ID0gNikgIAogIAogICMgUGxvdAogIGdldF9tYW4oZGYsIHRyYWl0ID0gdHJhaXQsIGNociA9ICJDSFIiLCBicCA9ICJQT1MiLCBzbnAgPSAiVE9QX1NOUCIsIHAgPSAiUCIpCiAgCiAgZGV2Lm9mZigpCn0pCmBgYAoKIyMjIyBDcmVhdGUgbGlzdCBvZiB0YXJnZXQgU05QcyB0byBleHRyYWN0IGZyb20gMUtHCgpgYGB7ciwgZXZhbCA9IEYsIHdhcm5pbmcgPSBGLCByZXN1bHRzID0gImhpZGUifQoKZGVzdF9kaXIgPSBoZXJlOjpoZXJlKCJkYXRhIiwgIjIwMjEwMTIyX3NucF9oaXRfbGlzdHMiKQoKIyBNYWtlIGRpcmVjdG9yeQpkaXIuY3JlYXRlKGRlc3RfZGlyKQogIAojIEp1c3QgU05QcyBmb3IgZXh0cmFjdGluZyBmcm9tIDFLRwpjb3VudGVyIDwtIDAKbGFwcGx5KGRhdGFfbGlzdCwgZnVuY3Rpb24ocGhlbm8pewogIGRmID0gcGhlbm9bWyJjbGVhbiJdXQogICMgU2V0IGNvdW50ZXIKICBjb3VudGVyIDw8LSBjb3VudGVyICsgMQogICMgU2V0IGZpbGUgYmFzZW5hbWUKICB0cmFpdCA9IG5hbWVzKGRhdGFfbGlzdClbY291bnRlcl0KICBmaWxlbmFtZSA9IHBhc3RlKHRyYWl0LCAiLmxpc3QiLCBzZXAgPSAiIikKICAjIFdyaXRlIFNOUHMgdG8gZmlsZQogIHJlYWRyOjp3cml0ZV9saW5lcyhkZiRUT1BfU05QLCBmaWxlLnBhdGgoZGVzdF9kaXIsIGZpbGVuYW1lKSkKfSkKCiMgU05QcyBhbmQgUC12YWx1ZXMgd2l0aCBoZWFkZXIgZm9yIGNsdW1waW5nIHdpdGggUGxpbmsKY291bnRlciA8LSAwCmxhcHBseShkYXRhX2xpc3QsIGZ1bmN0aW9uKHBoZW5vKXsKICBkZiA9IHBoZW5vW1siY2xlYW4iXV0KICAjIFNldCBjb3VudGVyCiAgY291bnRlciA8PC0gY291bnRlciArIDEKICAjIFNldCBmaWxlIGJhc2VuYW1lCiAgdHJhaXQgPSBuYW1lcyhkYXRhX2xpc3QpW2NvdW50ZXJdCiAgZmlsZW5hbWUgPSBwYXN0ZSh0cmFpdCwgIl93aXRoX1AudHh0Iiwgc2VwID0gIiIpCiAgIyBXcml0ZSBTTlBzIHRvIGZpbGUKICBkZiAlPiUgCiAgICBkcGx5cjo6c2VsZWN0KFNOUCA9IFRPUF9TTlAsIFApICU+JSAKICAgIHJlYWRyOjp3cml0ZV90c3YoZmlsZS5wYXRoKGRlc3RfZGlyLCBmaWxlbmFtZSkpCn0pCmBgYAoKIyMgRmlsdGVyIDFLRyBWQ0YgZm9yIHRhcmdldCBTTlBzCgpgYGB7ciwgZW5naW5lPSdiYXNoJywgZXZhbCA9IEZ9Cgp0cmFpdHM9JChlY2hvIGhlaSBibWkgZWR1IGludCBpYmQgcGlnKQpyZWY9Li4vcmVmcy9oczM3ZDUuZmEuZ3oKaW5fdmNmPS4uL3ZjZnMvMWtnX2FsbC52Y2YuZ3oKc25wc19kaXI9ZGF0YS8yMDIxMDEyMl9zbnBfaGl0X2xpc3RzCm91dF9kaXI9ZGF0YS8yMDIxMDEyNV9zbnBfaGl0c19maWx0ZXJlZAoKbWtkaXIgLXAgJG91dF9kaXIKCmZvciB0cmFpdCBpbiAkKGVjaG8gJHRyYWl0cyApOyBkbwogIGJzdWIgXAogICAgLU0gMTAwMDAgXAogICAgLW8gLi4vbG9nLzIwMjEwMTIyX2V4dHJhY3Rfc25wc18kdHJhaXQub3V0IFwKICAgIC1lIC4uL2xvZy8yMDIxMDEyMl9leHRyYWN0X3NucHNfJHRyYWl0LmVyciBcCiAgICAiIiIKICAgIGNvbmRhIGFjdGl2YXRlIGZzdF9lbnZfcmhlbCA7CiAgICBnYXRrIFNlbGVjdFZhcmlhbnRzIFwKICAgICAgLVIgJHJlZiBcCiAgICAgIC1WICRpbl92Y2YgXAogICAgICAtLWtlZXAtaWRzICRzbnBzX2Rpci8kdHJhaXQubGlzdCBcCiAgICAgIC1PICRvdXRfZGlyLyR0cmFpdC52Y2YuZ3ogCiAgICAiIiIgOwpkb25lCmBgYAoKIyMgR2V0IGFsbGVsZSBmcmVxdWVuY2llcyBvZiBTTlAgaGl0cyB3aXRoIGBQbGluazJgCgojIyMgSW1wb3J0IDFLRyBtZXRhZGF0YSAoZm9yIHNhbXBsZS1wb3B1bGF0aW9uIGtleSkKCkRvd25sb2RlZCB2aWEgdGhpcyBwYWdlOiA8aHR0cDovL3d3dy5pbnRlcm5hdGlvbmFsZ2Vub21lLm9yZy9kYXRhPi4KCkRvd25sb2FkIGxpbms6IDxodHRwOi8vZnRwLjEwMDBnZW5vbWVzLmViaS5hYy51ay92b2wxL2Z0cC90ZWNobmljYWwvd29ya2luZy8yMDEzMDYwNl9zYW1wbGVfaW5mby8yMDEzMDYwNl9zYW1wbGVfaW5mby54bHN4Pi4gCgpTYXZlZCBoZXJlOiBgZGF0YS8yMDEzMDYwNl9zYW1wbGVfaW5mby54bHN4YAoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHMgPSAnYXNpcyd9CnNhbXBsZXNfZmlsZSA9IGhlcmU6OmhlcmUoImRhdGEiLCAiMjAxMzA2MDZfc2FtcGxlX2luZm8ueGxzeCIpCgptZXRhID0gcmVhZHhsOjpyZWFkX3hsc3goc2FtcGxlc19maWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgc2hlZXQgPSAiU2FtcGxlIEluZm8iKSAlPiUKICBkcGx5cjo6c2VsZWN0KFNhbXBsZSwgUG9wdWxhdGlvbiwgR2VuZGVyKQoKa25pdHI6OmthYmxlKGhlYWQobWV0YSkpCmBgYAoKIyMjIFdyaXRlIHBvcHVsYXRpb24gZmlsZSBmb3IgUGxpbmsyCgpgYGB7ciwgZXZhbCA9IEZ9CnNhbXBsZV9wb3BuX2tleV9maWxlID0gaGVyZTo6aGVyZSgiZGF0YSIsICJwbGluazJfc2FtcGxlX3BvcG5fa2V5LnR4dCIpCgp3cml0ZS50YWJsZShtZXRhWywgMToyXSwKICAgICAgICAgICAgc2FtcGxlX3BvcG5fa2V5X2ZpbGUsCiAgICAgICAgICAgIHF1b3RlID0gRiwKICAgICAgICAgICAgc2VwID0gIlx0IiwKICAgICAgICAgICAgcm93Lm5hbWVzID0gRiwKICAgICAgICAgICAgY29sLm5hbWVzID0gRikKYGBgCgojIyMgUnVuIGBQbGluazJgIGZvciBTTlAgaGl0cwoKKFRha2Ugb25seSBiaWFsbGVsaWMgU05Qcy4pCgpgYGB7ciwgZW5naW5lPSdiYXNoJywgZXZhbCA9IEZ9Cgpjb25kYSBhY3RpdmF0ZSBmc3RfZW52X3JoZWwKCiMgU2V0IHZhcmlhYmxlcwoKdHJhaXRzPSQoZWNobyBoZWkgYm1pIGVkdSBpbnQgaWJkIHBpZykKaW5fdmNmX2Rpcj1kYXRhLzIwMjEwMTI1X3NucF9oaXRzX2ZpbHRlcmVkCnBvcG5fZmlsZT1kYXRhL3BsaW5rMl9zYW1wbGVfcG9wbl9rZXkudHh0Cm91dF9kaXI9ZGF0YS8yMDIxMDEyMl9zbnBfaGl0c19hbGZyZXFzCgojIFNldCB1cCBkaXJlY3Rvcmllcwpta2RpciAtcCAkb3V0X2RpcgoKZm9yIHRyYWl0IGluICQoZWNobyAkdHJhaXRzKTsgZG8KICBta2RpciAtcCAkb3V0X2Rpci8kdHJhaXQ7IApkb25lICAgCgojIFJ1biBQbGluazIKCiMjIEdldCBnbG9iYWwgQUYKZm9yIHRyYWl0IGluICQoZWNobyAkdHJhaXRzKTsgZG8KICBwbGluazIgXAogICAgLS12Y2YgJGluX3ZjZl9kaXIvJHRyYWl0LnZjZi5neiBcCiAgICAtLWZyZXEgXAogICAgLS1tYXgtYWxsZWxlcyAyIFwKICAgIC0tc25wcy1vbmx5IFwKICAgIC0tb3V0ICRvdXRfZGlyLyR0cmFpdC8kdHJhaXQuYWxsCmRvbmUKCiMjIEdldCBBRiBwZXIgcG9wdWxhdGlvbgpmb3IgdHJhaXQgaW4gJChlY2hvICR0cmFpdHMpOyBkbwogIHBsaW5rMiBcCiAgICAtLXZjZiAkaW5fdmNmX2Rpci8kdHJhaXQudmNmLmd6IFwKICAgIC0tZnJlcSBcCiAgICAtLW1heC1hbGxlbGVzIDIgXAogICAgLS1zbnBzLW9ubHkgXAogICAgLS1waGVubyBpaWQtb25seSAkcG9wbl9maWxlIFwKICAgIC0tbG9vcC1jYXRzIFBIRU5PMSBcCiAgICAtLW91dCAkb3V0X2Rpci8kdHJhaXQvJHRyYWl0IDsKZG9uZQpgYGAKCiMjIyBBZGQgYWxsZWxlIGZyZXF1ZW5jeSBmaWxlcwoKYGBge3J9CnRhcmdldF9kaXIgPSBoZXJlOjpoZXJlKCJkYXRhIiwgIjIwMjEwMTIyX3NucF9oaXRzX2FsZnJlcXMiKQoKIyBHbG9iYWwKY291bnRlciA8LSAwCmRhdGFfbGlzdCA9IGxhcHBseShkYXRhX2xpc3QsIGZ1bmN0aW9uKHBoZW5vKXsKICAjIHNldCBjb3VudGVyIAogIGNvdW50ZXIgPDwtIGNvdW50ZXIgKyAxCiAgIyBnZXQgdHJhaXQgbmFtZQogIHRyYWl0ID0gbmFtZXMoZGF0YV9saXN0KVtjb3VudGVyXQogICMgZ2V0IGZpbGUgcGF0aAogIHRhcmdldF9wYXRoID0gZmlsZS5wYXRoKHRhcmdldF9kaXIsIHRyYWl0LCBwYXN0ZSh0cmFpdCwgIi5hbGwuYWZyZXEiLCBzZXAgPSAiIikpCiAgIyByZWFkIGluIGRhdGEKICBjbGVhbl9hZiA9IHJlYWRfYWZyZXEodGFyZ2V0X3BhdGgpCiAgIyBhZGQgUE9QTiBjb2x1bW4KICBjbGVhbl9hZiRQT1BOID0gImFsbCIKICAjIGFkZCB0byBsaXN0CiAgcGhlbm9bWyJjbGVhbl9hZiJdXSA9IGNsZWFuX2FmCiAgCiAgcmV0dXJuKHBoZW5vKQp9KQoKIyBQZXIgcG9wdWxhdGlvbgpjb3VudGVyIDwtIDAKZGF0YV9saXN0ID0gbGFwcGx5KGRhdGFfbGlzdCwgZnVuY3Rpb24ocGhlbm8pewogICMgc2V0IGNvdW50ZXIKICBjb3VudGVyIDw8LSBjb3VudGVyICsgMQogICMgZ2V0IHRyYWl0IG5hbWUKICB0cmFpdCA9IG5hbWVzKGRhdGFfbGlzdClbY291bnRlcl0KICAjIGdldCBmaWxlIHBhdGgKICB0YXJnZXRfZmlsZXMgPSBsaXN0LmZpbGVzKGZpbGUucGF0aCh0YXJnZXRfZGlyLCB0cmFpdCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gIi4qW15hbGxdXFwuYWZyZXEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVsbC5uYW1lcyA9IFQpCiAgIyBnZXQgcG9wbiBuYW1lcwogIG5hbWVzKHRhcmdldF9maWxlcykgPSBiYXNlbmFtZSh0YXJnZXRfZmlsZXMpICU+JSAKICAgIHN0cl9zcGxpdCgiXFwuIiwgc2ltcGxpZnkgPSBUKSAlPiUgCiAgICBzdWJzZXQoc2VsZWN0ID0gMikKICAjIHJlYWQgZmlsZXMgYW5kIGJpbmQgaW50byBzaW5nbGUgREYKICBwb3BuX2FmcmVxcyA9IGxhcHBseSh0YXJnZXRfZmlsZXMsIGZ1bmN0aW9uKHBvcG4pewogICAgZGYgPSByZWFkX2FmcmVxKHBvcG4pCiAgfSkgJT4lIAogICAgZHBseXI6OmJpbmRfcm93cyguaWQgPSAiUE9QTiIpIyAlPiUgCiMgICAgZHBseXI6OnNlbGVjdCgtT0JTX0NUKSAlPiUgCiMgICAgdGlkeXI6OnBpdm90X3dpZGVyKGlkX2NvbHMgPSBTTlAsIG5hbWVzX2Zyb20gPSBQT1BOLCB2YWx1ZXNfZnJvbSA9IEFMVF9GUkVRUykKICAjIGNvbWJpbmUgd2l0aCBgY2xlYW5fYWZgCiAgcGhlbm9bWyJjbGVhbl9hZiJdXSA9IGRwbHlyOjpiaW5kX3Jvd3MocGhlbm9bWyJjbGVhbl9hZiJdXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3BuX2FmcmVxcykKICAKICByZXR1cm4ocGhlbm8pCn0pCmBgYAoKIyMgU2V0IHVwIG5lZ2F0aXZlIGNvbnRyb2xzCgpQdWxsIG91dCByYW5kb20gU05QcyB3aXRoIHRoZSBzYW1lIGdsb2JhbCBhbGxlbGUgZnJlcXVlbmNpZXMgYXMgdGhlIEdXQVMgU05QLWhpdHMKCiMjIyBCaW4gU05QIGhpdHMgYnkgYWxsZWxlIGZyZXF1ZW5jeQoKQmluZCB0byBjbGVhbiBERiB0byBnZXQgQUZzIG9mIHJpc2sgYWxsZWxlCgpbKipOT1RFKipde2NvbG9yPSJyZWQifTogSWYgYFJJU0tfQUxMRUxFYCBpcyB1bmtub3duLCBzZXQgdGhlIGFsbGVsZSBmcmVxdWVuY3kgdG8gYEFMVF9GUkVRU2AKCmBgYHtyfQpkYXRhX2xpc3QgPSBsYXBwbHkoZGF0YV9saXN0LCBmdW5jdGlvbihwaGVubyl7CiAgIyBqb2luIERGcwogIGRmID0gZHBseXI6OmxlZnRfam9pbihwaGVub1tbImNsZWFuIl1dLAogICAgICAgICAgICAgICAgICAgICAgICBkcGx5cjo6c2VsZWN0KHBoZW5vW1siY2xlYW5fYWYiXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLUNIUiksCiAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygiVE9QX1NOUCIgPSAiU05QIikpCiAgIyBnZXQgQUYgb2YgcmlzayBhbGxlbGUKICBkZiRSSVNLX0FGID0gZHBseXI6OmlmX2Vsc2UoZGYkUklTS19BTExFTEUgPT0gIj8iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZiRBTFRfRlJFUVMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRwbHlyOjppZl9lbHNlKGRmJFJJU0tfQUxMRUxFID09IGRmJEFMVCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGYkQUxUX0ZSRVFTLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAxIC0gZGYkQUxUX0ZSRVFTKSkKICAjIGFkZCBgSElUX0NPTlRST0xgIGNvbHVtbgogIGRmJEhJVF9DT05UUk9MID0gImhpdCIKICAjIGFkZCB0byBsaXN0CiAgcGhlbm9bWyJjb25zb2wiXV0gPSBkZgogIAogIHJldHVybihwaGVubykKfSkKYGBgCgoKQmluIGJ5IHJpc2sgYWxsZWxlIGZyZXF1ZW5jeQoKYGBge3J9CiMgMSUgaW50ZXJ2YWxzCmJyZWFrcG9pbnRzID0gc2VxKDAsIDEsIDAuMDEpCgpkYXRhX2xpc3QgPSBsYXBwbHkoZGF0YV9saXN0LCBmdW5jdGlvbihwaGVubyl7CiAgIyBjaG9vc2UgREYKICBkZiA9IHBoZW5vW1siY29uc29sIl1dCiAgIyBhZGQgYmlucwogIGRmJEJJTl8xMDAgPSBjdXQoZGYkUklTS19BRiwgYnJlYWtzID0gYnJlYWtwb2ludHMsIGxhYmVscyA9IEYpCiAgIyBzYXZlIGJhY2sgaW50byBsaXN0CiAgcGhlbm9bWyJjb25zb2wiXV0gPSBkZgogIAogIHJldHVybihwaGVubykKfSkKCmBgYAoKRXh0cmFjdCBrZXkgY29sdW1ucyBhbmQgd3JpdGUgdG8gZmlsZQoKYGBge3IsIGV2YWwgPSBGLCBtZXNzYWdlPUZ9Cm91dF9kaXIgPSBoZXJlOjpoZXJlKCJkYXRhIiwgIjIwMjEwMTI2X3NucF9yaXNrX2hpdHNfYmlubmVkIikKCmRpci5jcmVhdGUob3V0X2RpcikKCiMgU2F2ZSBsaXN0CmNvdW50ZXIgPC0gMApyaXNrX2FmcyA9IGxhcHBseShkYXRhX2xpc3QsIGZ1bmN0aW9uKHBoZW5vKXsKICAjIHNldCBjb3VudGVyIAogIGNvdW50ZXIgPDwtIGNvdW50ZXIgKyAxCiAgIyBnZXQgdGFyZ2V0IERGCiAgZGYgPSBwaGVub1tbImNvbnNvbCJdXQogICMgZmlsdGVyCiAgZGYgPSBkZiAlPiUgCiAgICBkcGx5cjo6ZmlsdGVyKFBPUE4gPT0gImFsbCIpICU+JSAjIHRha2Ugb25seSBnbG9iYWwgQUZzIAogICAgZHBseXI6OnNlbGVjdChUT1BfU05QLCBCSU5fMTAwKSAlPiUgCiAgICB0aWR5cjo6ZHJvcF9uYSgpICMgZHJvcCBOQXMKICAjIHNldCBvdXRwdXQgcGF0aAogIHRyYWl0ID0gbmFtZXMoZGF0YV9saXN0KVtjb3VudGVyXQogIHBhdGhfb3V0ID0gZmlsZS5wYXRoKG91dF9kaXIsIHBhc3RlKHRyYWl0LCAiLnR4dCIsIHNlcCA9ICIiKSkKICAjIHdyaXRlIHRvIGZpbGUKICByZWFkcjo6d3JpdGVfdHN2KGRmLCBwYXRoX291dCkKfSkKCgpgYGAKCiMjIyBCaW4gMUtHIFNOUHMgCgojIyMjIEdldCBhbGxlbGUgZnJlcXVlbmNpZXMgZnJvbSAxS0cKCldpdGggYFBsaW5rMmAsIHBlciBjaHJvbW9zb21lIGZvciBzcGVlZC4KCmBgYHtyLCBlbmdpbmU9J2Jhc2gnLCBldmFsPUZ9CiMgc2V0IG91dHB1dCBkaXJlY3RvcnkKaW5fZmlsZT0uLi92Y2ZzLzFrZ19hbGwudmNmLmd6Cm91dF9kaXI9Li4vYmlnX2RhdGEvMjAyMTAxMjVfYWxmcmVxc19hbGwKCm1rZGlyIC1wICRvdXRfZGlyCgojIFBlciBjaHJvbW9zb21lCmZvciBjaHIgaW4gJChzZXEgMSAyMikgOyBkbwogICMgY3JlYXRlIGFsbGVsZS1mcmVxIHRhYmxlcwogIGJzdWIgXAogICAgLU0gMTAwMDAgXAogICAgLW8gLi4vbG9nLzIwMjEwMTI1X3BsaW5rX2FsZnJlcV8kY2hyLm91dCBcCiAgICAtZSAuLi9sb2cvMjAyMTAxMjVfcGxpbmtfYWxmcmVxXyRjaHIuZXJyIFwKICAgICIiIgogICAgY29uZGEgYWN0aXZhdGUgZnN0X2Vudl9yaGVsIDsKICAgIHBsaW5rMiBcCiAgICAgIC0tdmNmICRpbl9maWxlIFwKICAgICAgLS1mcmVxIFwKICAgICAgLS1jaHIgJGNociBcCiAgICAgIC0tbWF4LWFsbGVsZXMgMiBcCiAgICAgIC0tc25wcy1vbmx5IFwKICAgICAgLS1vdXQgJG91dF9kaXIvJGNociAiOwpkb25lIApgYGAKCiMjIyMgQmluIHRoZW0gYW5kIHNhdmUgdG8gc2luZ2xlIGZpbGUKCmBgYHtyLCBldmFsID0gRn0KIyBPbiBjbHVzdGVyCgpsaWJyYXJ5KGhlcmUpCnNvdXJjZShoZXJlOjpoZXJlKCJjb2RlIiwgInNjcmlwdHMiLCAic291cmNlLlIiKSkKCiMgU2V0IHZhcmlhYmxlcwppbl9kaXIgPSAiLi4vYmlnX2RhdGEvMjAyMTAxMjVfYWxmcmVxc19hbGwiCm91dF9kaXIgPSAiLi4vYmlnX2RhdGEvMjAyMTAxMjVfYWxmcmVxc19hbGxfYmlubmVkIgpicmVha3BvaW50cyA9IHNlcSgwLCAxLCAwLjAxKSAjIDElIGJpbnMKCiMgQ3JlYXRlIG91dHB1dCBkaXJlY3RvcnkKZGlyLmNyZWF0ZShvdXRfZGlyKQoKIyBHZXQgbGlzdCBvZiBpbnB1dCBmaWxlcwppbl9maWxlcyA9IGxpc3QuZmlsZXMoaW5fZGlyLCBwYXR0ZXJuID0gIi5hZnJlcSIsIGZ1bGwubmFtZXMgPSBUKQoKIyBSZWFkIGluIGZpbGVzLCBhZGQgYmlucywgYW5kIHdyaXRlIHRvIG91dHB1dApmcmVxX2xpc3QgPSBsYXBwbHkoaW5fZmlsZXMsIGZ1bmN0aW9uKGNocl9maWxlKXsKICAjIHJlYWQgaW4gZmlsZQogIGRmID0gcmVhZF9hZnJlcShjaHJfZmlsZSkKICAjIGFkZCBiaW5zCiAgZGYkQklOXzEwMCA9IGN1dChkZiRBTFRfRlJFUVMsIGJyZWFrcyA9IGJyZWFrcG9pbnRzLCBsYWJlbHMgPSBGKQogICMgd3JpdGUgZmlsZQogIHJlYWRyOjp3cml0ZV90c3YoZGYsIGZpbGUgPSBmaWxlLnBhdGgob3V0X2RpciwgYmFzZW5hbWUoY2hyX2ZpbGUpKSkKfSkKCiMgQ29tYmluZSBpbnRvIHNpbmdsZSBERgpmcmVxX2RmID0gZHBseXI6OmJpbmRfcm93cyhmcmVxX2xpc3QpCgojIFdyaXRlIHRvIGZpbGUKcmVhZHI6OndyaXRlX3RzdihmcmVxX2RmLCBmaWxlID0gZmlsZS5wYXRoKG91dF9kaXIsICJhbGwuYWZyZXEiKSkKYGBgCgojIyMjIFB1bGwgb3V0IHJhbmRvbSBTTlBzIHdpdGggc2FtZSBBRiBhcyB0cmFpdCByaXNrIGFsbGVsZXMKCmBgYHtyLCBldmFsID0gRn0KIyBPbiBjbHVzdGVyCgpsaWJyYXJ5KGhlcmUpCnNvdXJjZShoZXJlOjpoZXJlKCJjb2RlIiwgInNjcmlwdHMiLCAic291cmNlLlIiKSkKCiMgVmFyaWFibGVzCgppbnB1dF9yaXNrX3NucF9kaXIgPSBoZXJlOjpoZXJlKCJkYXRhIiwgIjIwMjEwMTI2X3NucF9yaXNrX2hpdHNfYmlubmVkIikKYWxsXzFrZ19iaW5zID0gIi4uL2JpZ19kYXRhLzIwMjEwMTI1X2FsZnJlcXNfYWxsX2Jpbm5lZC9hbGwuYWZyZXEiCmluaXRpYWxfc2VlZCA9IDEyMwpvdXRwdXRfZGlyID0gaGVyZTo6aGVyZSgiZGF0YSIsICIyMDIxMDEyNl9yYW5kb21fc25wcyIpCm91dHB1dF9kaXJfc25waWRzID0gaGVyZTo6aGVyZSgiZGF0YSIsICIyMDIxMDEyNl9yYW5kb21fc25wc19zbnBfaWRzIikKCmRpci5jcmVhdGUob3V0cHV0X2RpcikKZGlyLmNyZWF0ZShvdXRwdXRfZGlyX3NucGlkcykKCiMjIFJlYWQgaW4gdGFyZ2V0IFNOUCBERiBhbmQgc3BsaXQgaW50byBsaXN0IGJ5IGJpbgpwaGVub3MgPSBnc3ViKCIudHh0IiwgIiIsIGJhc2VuYW1lKGxpc3QuZmlsZXMoaW5wdXRfcmlza19zbnBfZGlyKSkpCm5hbWVzKHBoZW5vcykgPSBwaGVub3MKCnJpc2tfbGlzdCA9IGxhcHBseShwaGVub3MsIGZ1bmN0aW9uKHBoZW5vKXsKICAjIHNldCBmaWxlIHBhdGgKICBmaWxlX3BhdGggPSBmaWxlLnBhdGgoaW5wdXRfcmlza19zbnBfZGlyLCBwYXN0ZShwaGVubywgIi50eHQiLCBzZXAgPSAiIikpCiAgIyByZWFkIGZpbGUKICBvdXQgPSByZWFkcjo6cmVhZF90c3YoZmlsZV9wYXRoLAogICAgICAgICAgICAgICAgICAgICAgICBjb2xfbmFtZXMgPSBUKSAlPiUgCiAgICBzcGxpdCguLCBmID0gLiRCSU5fMTAwKSAjIHNwbGl0IGJ5IGJpbgp9KQogIAojIyBSZWFkIGluIDFLRyBkYXRhCmZyZXFfZGYgPSByZWFkcjo6cmVhZF90c3YoYWxsXzFrZ19iaW5zKQoKIyBGb3IgZWFjaCBiaW4gaW4gYHJpc2tfbGlzdGAsIHB1bGwgb3V0IHRoZSBzYW1lIG51bWJlciBvZiByYW5kb20gbnVtYmVyIDFLRyBTTlBzIHdpdGggdGhlIHNhbWUgYmluCgojIyBTZXQgc2VlZApzZXQuc2VlZChpbml0aWFsX3NlZWQpCgojIyBHZXQgc2VlZHMgZm9yIGVhY2ggYmluCnNlZWRzID0gc2FtcGxlKDE6MTAwMCwgbGVuZ3RoKHJpc2tfbGlzdCkpCgojIyBSdW4gb3ZlciBsaXN0CmNvdW50ZXIgPC0gMApsYXBwbHkocmlza19saXN0LCBmdW5jdGlvbihwaGVubyl7CiAgIyBzZXQgY291bnRlciAKICBjb3VudGVyIDw8LSBjb3VudGVyICsgMSAgCiAgIyBzZXQgc2VlZCBmb3IgcGhlbm8KICBzZXQuc2VlZChzZWVkcylbY291bnRlcl0KICAjIGdldCBzZWVkcyBmb3IgYmluCiAgYmluX3NlZWRzID0gc2FtcGxlKDE6MTAwMCwgbGVuZ3RoKHBoZW5vKSkKICAjIGdldCByYW5kb20gU05QcyBmcm9tIGVhY2ggYmluCiAgYmluX2NvdW50ZXIgPC0gMAogIG91dCA9IGxhcHBseShwaGVubywgZnVuY3Rpb24oYmluX2RmKXsKICAgICMgc2V0IGBiaW5fY291bnRlcmAKICAgIGJpbl9jb3VudGVyIDw8LSBiaW5fY291bnRlciArIDEKICAgICMgZ2V0IHRhcmdldCBiaW4KICAgIHRhcmdldF9iaW4gPSBhcy5pbnRlZ2VyKG5hbWVzKHBoZW5vKVtiaW5fY291bnRlcl0pCiAgICAjIGdldCBudW1iZXIgb2YgbWF0Y2hlcyByZXF1aXJlZAogICAgaGl0c19uID0gbnJvdyhiaW5fZGYpCiAgICAjIHNldCBzZWVkCiAgICBzZXQuc2VlZChiaW5fc2VlZHNbYmluX2NvdW50ZXJdKSAgICAKICAgICMgZmlsdGVyIDFrZyBERiBmb3IgU05QcyB3aXRoIHNhbWUgYmluIGFuZCBnZXQgcmFuZG9tIGhpdHMKICAgIHJhbmRvbV9oaXRzID0gZnJlcV9kZiAlPiUgCiAgICAgICNkcGx5cjo6c2VsZWN0KFNOUCwgQUxUX0ZSRVFTLCBPQlNfQ1QsIEJJTl8xMDApICU+JSAKICAgICAgZHBseXI6OmZpbHRlcihCSU5fMTAwID09IHRhcmdldF9iaW4pICU+JSAKICAgICAgZHBseXI6OnNsaWNlX3NhbXBsZShuID0gaGl0c19uKSAlPiUgCiAgICAgIGRwbHlyOjpyZW5hbWUoUkFORE9NX1NOUCA9IFNOUCwKICAgICAgICAgICAgICAgICAgICBSQU5ET01fQklOXzEwMCA9IEJJTl8xMDApCiAgICAjIGJpbmQgYHJhbmRvbV9oaXRzYCB0byB0YXJnZXQgU05QIGRmCiAgICBkZl9vdXQgPSBjYmluZChiaW5fZGYsIHJhbmRvbV9oaXRzKQogICAgCiAgICByZXR1cm4oZGZfb3V0KQogIH0pICU+JSAKICAgICMgYmluZCBpbnRvIHNpbmdsZSBkYXRhIGZyYW1lCiAgICBkcGx5cjo6YmluZF9yb3dzKCkKICAKICAjIHNhdmUgdG8gZmlsZQogICMjIHNldCBvdXRwdXQgcGF0aAogIHRyYWl0ID0gbmFtZXMocmlza19saXN0KVtjb3VudGVyXQogIG91dF9wYXRoID0gZmlsZS5wYXRoKG91dHB1dF9kaXIsIHBhc3RlKHRyYWl0LCAiLnR4dCIsIHNlcCA9ICIiKSkKICAjIyB3cml0ZSBmaWxlCiAgcmVhZHI6OndyaXRlX3RzdihvdXQsIG91dF9wYXRoKQogIAogICMgc2F2ZSBqdXN0IFNOUCBJRHMgKGZvciBQbGluayB0byBnZXQgcGVyLXBvcHVsYXRpb24gQUZzKQogIG91dF9wYXRoID0gZmlsZS5wYXRoKG91dHB1dF9kaXJfc25waWRzLCBwYXN0ZSh0cmFpdCwgIi5saXN0Iiwgc2VwID0gIiIpKQogICMjIHdyaXRlIGZpbGUKICByZWFkcjo6d3JpdGVfbGluZXMob3V0JFJBTkRPTV9TTlAsIG91dF9wYXRoKSAgCn0pCgpgYGAKCiMjIyMgRmlsdGVyIFZDRnMgZm9yIHJhbmRvbSBTTlBzCgpgYGB7YmFzaCwgZXZhbCA9IEZ9CnRyYWl0cz0kKGVjaG8gaGVpIGJtaSBlZHUgaW50IGliZCBwaWcpCnJlZj0uLi9yZWZzL2hzMzdkNS5mYS5negppbl92Y2Y9Li4vdmNmcy8xa2dfYWxsLnZjZi5negpzbnBzX2Rpcj1kYXRhLzIwMjEwMTI2X3JhbmRvbV9zbnBzX3NucF9pZHMKb3V0X2Rpcj1kYXRhLzIwMjEwMTI3X3NucF9ybmRtX2ZpbHRlcmVkCgpta2RpciAtcCAkb3V0X2RpcgoKZm9yIHRyYWl0IGluICQoZWNobyAkdHJhaXRzICk7IGRvCiAgYnN1YiBcCiAgICAtTSAxMDAwMCBcCiAgICAtbyAuLi9sb2cvMjAyMTAxMjdfZXh0cmFjdF9zbnBzXyR0cmFpdC5vdXQgXAogICAgLWUgLi4vbG9nLzIwMjEwMTI3X2V4dHJhY3Rfc25wc18kdHJhaXQuZXJyIFwKICAgICIiIgogICAgY29uZGEgYWN0aXZhdGUgZnN0X2Vudl9yaGVsIDsKICAgIGdhdGsgU2VsZWN0VmFyaWFudHMgXAogICAgICAtUiAkcmVmIFwKICAgICAgLVYgJGluX3ZjZiBcCiAgICAgIC0ta2VlcC1pZHMgJHNucHNfZGlyLyR0cmFpdC5saXN0IFwKICAgICAgLU8gJG91dF9kaXIvJHRyYWl0LnZjZi5neiAKICAgICIiIiA7CmRvbmUKYGBgCgojIyMjIEdldCBwZXItcG9wdWxhdGlvbiBhbGxlbGUgZnJlcXVlbmNpZXMgb2YgcmFuZG9tIFNOUHMgCgpgYGB7YmFzaCwgZXZhbCA9IEZ9CnRyYWl0cz0kKGVjaG8gaGVpIGJtaSBlZHUgaW50IGliZCBwaWcpCnJhbmRvbV9zbnBzX2Rpcj1kYXRhLzIwMjEwMTI2X3JhbmRvbV9zbnBzX3NucF9pZHMKdmNmX2luX2Rpcj1kYXRhLzIwMjEwMTI3X3NucF9ybmRtX2ZpbHRlcmVkCnBvcG5fa2V5PWRhdGEvcGxpbmsyX3NhbXBsZV9wb3BuX2tleS50eHQKb3V0X2Rpcj1kYXRhLzIwMjEwMTI3X3NucF9ybmRtX2FsZnJlcXMKCgpmb3IgdHJhaXQgaW4gJChlY2hvICR0cmFpdHMgKTsgZG8KCiAgbWtkaXIgLXAgJG91dF9kaXIvJHRyYWl0CiAgCiAgYnN1YiBcCiAgICAtTSAxMDAwMCBcCiAgICAtbyAuLi9sb2cvMjAyMTAxMjdfcmRtX3BvcG5fYWZyZXFzXyR0cmFpdC5vdXQgXAogICAgLWUgLi4vbG9nLzIwMjEwMTI3X3JkbV9wb3BuX2FmcmVxc18kdHJhaXQuZXJyIFwKICAgICIiIgogICAgY29uZGEgYWN0aXZhdGUgZnN0X2Vudl9yaGVsIDsKICAgIHBsaW5rMiBcCiAgICAgIC0tdmNmICR2Y2ZfaW5fZGlyLyR0cmFpdC52Y2YuZ3ogXAogICAgICAtLWV4dHJhY3QgJHJhbmRvbV9zbnBzX2Rpci8kdHJhaXQubGlzdCBcCiAgICAgIC0tZnJlcSBcCiAgICAgIC0tcGhlbm8gaWlkLW9ubHkgJHBvcG5fa2V5IFwKICAgICAgLS1sb29wLWNhdHMgUEhFTk8xIFwKICAgICAgLS1vdXQgJG91dF9kaXIvJHRyYWl0LyR0cmFpdAogICAgIiIiIDsKZG9uZSAgCmBgYAoKIyMjIyBCaW5kIGludG8gc2luZ2xlIERGCgpgYGB7ciwgbWVzc2FnZSA9IEZ9CmluX2Rpcl9ybmRtID0gaGVyZTo6aGVyZSgiZGF0YSIsICIyMDIxMDEyNl9yYW5kb21fc25wcyIpCmluX2Rpcl9hZnJlcSA9IGhlcmU6OmhlcmUoImRhdGEiLCAiMjAyMTAxMjdfc25wX3JuZG1fYWxmcmVxcyIpCgojIFJlYWQgaW4gZGF0YQoKIyMgUmFuZG9tIFNOUHMKaW5fZmlsZXNfcm5kbSA9IGxpc3QuZmlsZXMoaW5fZGlyX3JuZG0sIGZ1bGwubmFtZXMgPSBUKQpuYW1lcyhpbl9maWxlc19ybmRtKSA9IGdzdWIoIi50eHQiLCAiIiwgYmFzZW5hbWUoaW5fZmlsZXNfcm5kbSkpCgpyYW5kb21fc25wcyA9IGxhcHBseShpbl9maWxlc19ybmRtLCBmdW5jdGlvbihmaWxlKXsKICBvdXQgPSByZWFkcjo6cmVhZF90c3YoZmlsZSkKICAjIGFkZCBgUE9QTmAgY29sdW1uCiAgb3V0JFBPUE4gPSAiYWxsIgogIAogIHJldHVybihvdXQpCn0pICU+JSAKICBkcGx5cjo6YmluZF9yb3dzKC5pZCA9ICJQSEVOTyIpCgojIyBQb3BuIGFmcmVxcwpwb3BuX2FmcmVxcyA9IGxhcHBseSh0cmFpdF9sZXZlbHMsIGZ1bmN0aW9uKHBoZW5vKXsKICB0YXJnZXRfZmlsZXMgPSBsaXN0LmZpbGVzKGZpbGUucGF0aChpbl9kaXJfYWZyZXEsIHBoZW5vKSwgcGF0dGVybiA9ICIuYWZyZXEiLCBmdWxsLm5hbWVzID0gVCkKICAKICBuYW1lcyh0YXJnZXRfZmlsZXMpID0gYmFzZW5hbWUodGFyZ2V0X2ZpbGVzKSAlPiUgCiAgICBzdHJfc3BsaXQoIlxcLiIsIHNpbXBsaWZ5ID0gVCkgJT4lIAogICAgc3Vic2V0KHNlbGVjdCA9IDIpCiAgCiAgcG9wbl9hZnJlcXMgPSBsYXBwbHkodGFyZ2V0X2ZpbGVzLCBmdW5jdGlvbihwb3BuKXsKICAgIGRmID0gcmVhZF9hZnJlcShwb3BuKQogIH0pICU+JSAKICAgIGRwbHlyOjpiaW5kX3Jvd3MoLmlkID0gIlBPUE4iKSAjICU+JSAKICAjICBkcGx5cjo6c2VsZWN0KC1PQlNfQ1QpICU+JSAKICAjICB0aWR5cjo6cGl2b3Rfd2lkZXIoaWRfY29scyA9IFNOUCwgbmFtZXNfZnJvbSA9IFBPUE4sIHZhbHVlc19mcm9tID0gYyhBTFRfRlJFUVMsIE9CU19DVCkpCiAgCiAgcmV0dXJuKHBvcG5fYWZyZXFzKQp9KSAlPiUgCiAgZHBseXI6OmJpbmRfcm93cyguaWQgPSAiUEhFTk8iKQoKIyBCaW5kIGBwb3BuX2FmcmVxc2AgdG8gYHJhbmRvbV9zbnBzYApyYW5kb21fYWZyZXFzID0gZHBseXI6OmZ1bGxfam9pbihkcGx5cjo6c2VsZWN0KHJhbmRvbV9zbnBzLCAtYyhQT1BOLCBBTFRfRlJFUVMsIE9CU19DVCkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3BuX2FmcmVxcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBjKCJQSEVOTyIsICJSQU5ET01fU05QIiA9ICJTTlAiLCAiUkVGIiwgIkFMVCIsICJDSFIiKSkKIyBCaW5kIGBhbGxgIEFGcwpyYW5kb21fYWZyZXFzID0gcmJpbmQocmFuZG9tX2FmcmVxcywgcmFuZG9tX3NucHMpCgojIyBSZWNvZGUgUEhFTk8KI3JhbmRvbV9hZnJlcXMkUEhFTk8gPSBkcGx5cjo6cmVjb2RlKHJhbmRvbV9hZnJlcXMkUEhFTk8sICEhIXJldl9yZWNvZGVfdmVjKQoKIyMgQWRkIGBISVRfQ09OVFJPTGAgY29sdW1uCnJhbmRvbV9hZnJlcXMkSElUX0NPTlRST0wgPSAiY29udHJvbCIKYGBgCgojIyMjIEFkZCB0byBgZGF0YV9saXN0YAoKYGBge3J9CmNvdW50ZXIgPC0gMApkYXRhX2xpc3QgPSBsYXBwbHkoZGF0YV9saXN0LCBmdW5jdGlvbihwaGVubyl7CiAgIyBzZXQgY291bnRlcgogIGNvdW50ZXIgPDwtIGNvdW50ZXIgKyAxCiAgIyBzZXQgdGFyZ2V0IHBoZW5vCiAgdGFyZ2V0X3BoZW5vID0gbmFtZXMoZGF0YV9saXN0KVtjb3VudGVyXQogICMgYWRkIHJhbmRvbSBhZnJlcXMKICByYW5kb21fYWYgPSByYW5kb21fYWZyZXFzICU+JSAKICAgIGRwbHlyOjpmaWx0ZXIoUEhFTk8gPT0gdGFyZ2V0X3BoZW5vKQogICMgcmVjb2RlIFBIRU5PCiAgcmFuZG9tX2FmJFBIRU5PID0gZHBseXI6OnJlY29kZShyYW5kb21fYWYkUEhFTk8sICEhIXJlY29kZV92ZWMpCiAgIyBhZGQgdG8gbGlzdAogIHBoZW5vW1sicmFuZG9tX2FmIl1dID0gcmFuZG9tX2FmCiAgCiAgcmV0dXJuKHBoZW5vKQp9KQpgYGAKCiMjIENsdW1wIHRvIGdldCBsZWFkIFNOUHMKClVzZSBgUGxpbmsxLjlgIChgUGxpbmsyLjBgIGRvZXNuJ3QgaGF2ZSBhIGBjbHVtcGAgZnVuY3Rpb24uKQoKRnJvbSB0aGUgYFBsaW5rMS43YCBkb2N1bWVudGF0aW9uICg8aHR0cDovL3p6ei5id2guaGFydmFyZC5lZHUvcGxpbmsvY2x1bXAuc2h0bWw+KSwgd2hpY2ggYXBwbGllcyB0byBgUGxpbmsxLjlgOgoKPiBUaGUgY2x1bXBpbmcgcHJvY2VkdXJlIHRha2VzIGFsbCBTTlBzIHRoYXQgYXJlIHNpZ25pZmljYW50IGF0IHRocmVzaG9sZCBwMSB0aGF0IGhhdmUgbm90IGFscmVhZHkgYmVlbiBjbHVtcGVkIChkZW5vdGluZyB0aGVzZSBhcyBpbmRleCBTTlBzKSBhbmQgZm9ybXMgY2x1bXBzIG9mIGFsbCBvdGhlciBTTlBzIHRoYXQgYXJlIHdpdGhpbiBhIGNlcnRhaW4ga2IgZGlzdGFuY2UgZnJvbSB0aGUgaW5kZXggU05QIChkZWZhdWx0IDI1MGtiKSBhbmQgdGhhdCBhcmUgaW4gbGlua2FnZSBkaXNlcXVpbGlicml1bSB3aXRoIHRoZSBpbmRleCBTTlAsIGJhc2VkIG9uIGFuIHItc3F1YXJlZCB0aHJlc2hvbGQgKGRlZmF1bHQgMC41MCkuLi4gVGhpcyBpcyBhIGdyZWVkeSBhbGdvcml0aG0gYW5kIHNvIGVhY2ggU05QIHdpbGwgb25seSBhcHBlYXIgaW4gYSBzaW5nbGUgY2x1bXAsIGlmIGF0IGFsbC4gCgo+IC4uLlt0XWhlIFRPVEFMIGZpZWxkIGxpc3RzIGFsbCBTTlBzIHRoYXQgYXJlIGNsdW1wZWQgd2l0aCB0aGUgaW5kZXggU05QLCBpcnJlc3BlY3RpdmUgb2YgdGhlIHAtdmFsdWUgZm9yIHRob3NlIFNOUHMuIFRoaXMgbnVtYmVyIGlzIHRoZW4gc3BsaXQgaW50byB0aG9zZSBjbHVtcGVkIFNOUHMgdGhhdCBhcmUgbm90IHNpZ25pZmljYW50IChwPjAuMDUpIGFuZCB2YXJpb3VzIG90aGVyIGdyb3VwcyBkZWZpbmVkIGJ5IHNpZ25pZmljYW5jZSB0aHJlc2hvbGRzLiBGb3IgU05QcyB0aGF0IGFyZSBzaWduaWZpY2FudCBhdCB0aGUgcDIgdGhyZXNob2xkLCB0aGV5IGFyZSBsaXN0ZWQgZXhwbGljaXRseS4gVGhlICgxKSBhZnRlciBlYWNoIFNOUCBuYW1lIHJlZmVycyB0byB0aGUgcmVzdWx0cyBmaWxlIHRoZXkgY2FtZSBmcm9tIChpbiB0aGlzIGNhc2UsIHRoZXJlIGlzIG9ubHkgYSBzaW5nbGUgcmVzdWx0IGZpbGUgc3BlY2lmaWVkLCBzbyBhbGwgdmFsdWVzIGFyZSAxKS4KCkhlcmUsIHdlJ3JlIHRha2luZyBhbGwgU05QcyB3aXRoICpQKiA8IDFlLTA4IGFzIGluZGV4IFNOUHMsIGFuZCBpdCB3aWxsIGV4cGxpY2l0bHkgbGlzdCBhbGwgU05QcyB3aXRoaW4gdGhlIGNsdW1wIHRoYXQgYWxzbyBtZWV0IHRoYXQgdGhyZXNob2xkLiAKCmBgYHtiYXNoLCBldmFsID0gRn0KCiMgQWN0aXZhdGUgZW52aXJvbm1lbnQKY29uZGEgYWN0aXZhdGUgZnN0X2Vudl9yaGVsCgojIFNldCB2YXJpYWJsZXMKdHJhaXRzPSQoZWNobyBoZWkgYm1pIGVkdSBpbnQgaWJkIHBpZykKaW5fdmNmX2Rpcj1kYXRhLzIwMjEwMTI1X3NucF9oaXRzX2ZpbHRlcmVkCnNucF9wX2Rpcj1kYXRhLzIwMjEwMTIyX3NucF9oaXRfbGlzdHMKb3V0X2Rpcj1kYXRhLzIwMjEwMTI1X2NsdW1wZWQKCnIyX3BhcmFtcz0kKGVjaG8gMC4xIDAuMiAwLjMpCmtiX3BhcmFtcz0kKGVjaG8gNTAwIDc1MCAxMDAwICkKCiMgTWFrZSBkaXJlY3RvcnkKbWtkaXIgLXAgJG91dF9kaXIKCiMgUnVuIHdpdGggZGlmZmVyZW50IHBhcmFtZXRlcnMKZm9yIHRyYWl0IGluICQoZWNobyAkdHJhaXRzICk7IGRvCgogIG1rZGlyIC1wICRvdXRfZGlyLyR0cmFpdCA7CiAgCiAgZm9yIHIyIGluICRyMl9wYXJhbXMgOyBkbwogICAgZm9yIGtiIGluICRrYl9wYXJhbXMgOyBkbwogICAgICBic3ViIFwKICAgICAgICAtbyAuLi9sb2cvMjAyMTAxMjVfY2x1bXBfJHRyYWl0XF8kcjJcXyRrYi5vdXQgXAogICAgICAgIC1lIC4uL2xvZy8yMDIxMDEyNV9jbHVtcF8kdHJhaXRcXyRyMlxfJGtiLmVyciBcCiAgICAgICAgIiIiCiAgICAgICAgY29uZGEgYWN0aXZhdGUgZnN0X2Vudl9yaGVsIDsKICAgICAgICBwbGluayBcCiAgICAgICAgICAtLXZjZiAkaW5fdmNmX2Rpci8kdHJhaXQudmNmLmd6IFwKICAgICAgICAgIC0tY2x1bXAgJHNucF9wX2Rpci8kdHJhaXRcX3dpdGhfUC50eHQgXAogICAgICAgICAgLS1jbHVtcC1wMSAwLjAwMDAwMDAxIFwKICAgICAgICAgIC0tY2x1bXAtcDIgMC4wMDAwMDAwMSBcCiAgICAgICAgICAtLWNsdW1wLXIyICRyMiBcCiAgICAgICAgICAtLWNsdW1wLWtiICRrYiBcCiAgICAgICAgICAtLW91dCAkb3V0X2Rpci8kdHJhaXQvcjItJHIyXF9rYi0ka2IgCiAgICAgICAgIiIiICA7CiAgICBkb25lIDsKICBkb25lOyAgCmRvbmUKCmBgYAoKIyMgRmlsdGVyIFZDRnMgZm9yIGNsdW1wZWQgU05QcwoKYGBge3IsIGVuZ2luZSA9ICdiYXNoJywgZXZhbCA9IEZ9CiMgT24gY2x1c3RlcgoKIyBTZXQgdmFyaWFibGVzCnRyYWl0cz0kKGVjaG8gaGVpIGJtaSBlZHUgaW50IGliZCBwaWcpCmNsdW1wX3BhcmFtPSJyMi0wLjFfa2ItMTAwMCIKaW5fZGlyX3NucD1kYXRhLzIwMjEwMTI1X2NsdW1wZWQKaW5fZGlyX3ZjZj1kYXRhLzIwMjEwMTI1X3NucF9oaXRzX2ZpbHRlcmVkCnJlZj0uLi9yZWZzL2hzMzdkNS5mYS5negpvdXRfZGlyX3NucF9saXN0PWRhdGEvMjAyMTAxMjhfY2x1bXBlZF9zbnBzCm91dF9kaXJfdmNmcz1kYXRhLzIwMjEwMTI4X2hpdHNfdmNmcwoKbWtkaXIgLXAgJG91dF9kaXJfc25wX2xpc3QgJG91dF9kaXJfdmNmcwoKIyBFeHRyYWN0IFNOUCBjb2x1bW4gYW5kIHdyaXRlIHRvIGxpc3QKZm9yIHRyYWl0IGluICQoZWNobyAkdHJhaXRzICk7IGRvCiAgdGFyZ2V0X2ZpbGU9JGluX2Rpcl9zbnAvJHRyYWl0LyRjbHVtcF9wYXJhbS5jbHVtcGVkIDsKICBhd2sgJ3twcmludCAkM30nICR0YXJnZXRfZmlsZSB8IHRhaWwgLW4rMiBcCiAgICA+ICRvdXRfZGlyX3NucF9saXN0LyR0cmFpdC5saXN0IDsKZG9uZQoKIyBNYWtlIG5ldyBWQ0ZzCmZvciB0cmFpdCBpbiAkKGVjaG8gJHRyYWl0cyApOyBkbwogIGJzdWIgXAogICAgLW8gLi4vbG9nLzIwMjEwMTI4X2ZpbHRlcl92Y2ZzX2NsdW1wXyR0cmFpdC5vdXQgXAogICAgLWUgLi4vbG9nLzIwMjEwMTI4X2ZpbHRlcl92Y2ZzX2NsdW1wXyR0cmFpdC5lcnIgXAogICAgIiIiCiAgICBjb25kYSBhY3RpdmF0ZSBmc3RfZW52X3JoZWwgOwogICAgZ2F0ayBTZWxlY3RWYXJpYW50cyBcCiAgICAgIC1SICRyZWYgXAogICAgICAtViAkaW5fZGlyX3ZjZi8kdHJhaXQudmNmLmd6IFwKICAgICAgLS1rZWVwLWlkcyAkb3V0X2Rpcl9zbnBfbGlzdC8kdHJhaXQubGlzdCBcCiAgICAgIC1PICRvdXRfZGlyX3ZjZnMvJHRyYWl0LnZjZi5neiAgICAgCiAgICAiIiIgOwpkb25lICAgIAoKYGBgCgojIyMgQWRkIGNsdW1wZWQgU05QIGZpbGVzIHRvIGBkYXRhX2xpc3RgCgpgYGB7cn0KdGFyZ2V0X2RpciA9IGhlcmU6OmhlcmUoImRhdGEiLCAiMjAyMTAxMjVfY2x1bXBlZCIpCgpjb3VudGVyIDwtIDAKZGF0YV9saXN0ID0gbGFwcGx5KGRhdGFfbGlzdCwgZnVuY3Rpb24ocGhlbm8pewogICMgc2V0IGNvdW50ZXIKICBjb3VudGVyIDw8LSBjb3VudGVyICsgMQogICMgZ2V0IHRyYWl0IG5hbWUKICB0cmFpdCA9IG5hbWVzKGRhdGFfbGlzdClbY291bnRlcl0KICAjIGdldCBmaWxlIHBhdGgKICB0YXJnZXRfZmlsZXMgPSBsaXN0LmZpbGVzKGZpbGUucGF0aCh0YXJnZXRfZGlyLCB0cmFpdCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gIi5jbHVtcGVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bGwubmFtZXMgPSBUKQogIG5hbWVzKHRhcmdldF9maWxlcykgPSBnc3ViKCIuY2x1bXBlZCIsICIiLCBiYXNlbmFtZSh0YXJnZXRfZmlsZXMpKQogICMgcmVhZCBmaWxlcyBhcyBgY2x1bXBlZGAKICBjb3VudGVyX2NsdW1wIDwtIDAKICBjbHVtcGVkID0gbGFwcGx5KHRhcmdldF9maWxlcywgZnVuY3Rpb24ocGFyYW1zKXsKICAgICMgc2V0IGNvdW50ZXIKICAgIGNvdW50ZXJfY2x1bXAgPDwtIGNvdW50ZXJfY2x1bXAgKyAxCiAgICAjIHNwbGl0IHBhcmFtcyBzdHJpbmcKICAgIHBhcmFtX3N0ciA9IG5hbWVzKHRhcmdldF9maWxlcylbY291bnRlcl9jbHVtcF0gJT4lIAogICAgICBzdHJpbmdyOjpzdHJfc3BsaXQoIl8iLCBzaW1wbGlmeSA9IFQpICU+JQogICAgICBzdHJpbmdyOjpzdHJfc3BsaXQoIi0iLCBzaW1wbGlmeSA9IFQpCiAgICAjIGdldCBwYXJhbXMKICAgIHIyID0gYXMubnVtZXJpYyhwYXJhbV9zdHJbMSwyXSkKICAgIGtiID0gYXMuaW50ZWdlcihwYXJhbV9zdHJbMiwyXSkKICAgICMgcmVhZCBmaWxlcwogICAgZGYgPSByZWFkLnRhYmxlKHBhcmFtcywgaGVhZGVyID0gVCkKICAgICMgYWRkIHBhcmFtcyB0byBERgogICAgZGYkcjIgPSByMgogICAgZGYka2IgPSBrYgogICAgCiAgICByZXR1cm4oZGYpCiAgfSkKICAjIGFkZCBgY2x1bXBlZGAgbGlzdCB0byBgZGF0YV9saXN0YAogIHBoZW5vW1siY2x1bXBlZCJdXSA9IGNsdW1wZWQKICAjIGJpbmQgYGNsdW1wZWRgIGludG8gc2luZ2xlIERGIGFuZCBhZGQgdG8gYGRhdGFfbGlzdGAKICBwaGVub1tbImNsdW1wZWRfYWxsIl1dID0gZHBseXI6OmJpbmRfcm93cyhjbHVtcGVkKQogIAogIHJldHVybihwaGVubykKfSkKYGBgCgojIyBFeHRyYWN0IHJhbmRvbSBTTlBzIGZpbHRlcmVkIGJ5IHRob3NlIG1hdGNoaW5nIHRoZSBjbHVtcGVkIFNOUHMKCmBgYHtyLCBldmFsID0gRn0Kb3V0X2RpciA9IGhlcmU6OmhlcmUoImRhdGEiLCAiMjAyMTAxMjhfcmFuZG9tX3NucHMiKQoKZGlyLmNyZWF0ZShvdXRfZGlyKQoKY291bnRlciA9IDAKb3V0ID0gbGFwcGx5KGRhdGFfbGlzdCwgZnVuY3Rpb24ocGhlbm8pewogICMgc2V0IGNvdW50ZXIKICBjb3VudGVyIDw8LSBjb3VudGVyICsgMQogICMgZ2V0IG5hbWUgb2YgdHJhaXQKICB0cmFpdCA9IG5hbWVzKGRhdGFfbGlzdClbY291bnRlcl0KICAjIGdldCBjbHVtcGVkIFNOUHMKICBjbHVtcGVkX3NucHMgPSBwaGVub1tbImNsdW1wZWQiXV1bW2NsdW1wX3BhcmFtXV0kU05QCiAgIyBnYXRoZXIgU05QcwogIHNucHMgPSBwaGVub1tbInJhbmRvbV9hZiJdXSAlPiUgCiAgICBkcGx5cjo6ZmlsdGVyKFRPUF9TTlAgJWluJSBjbHVtcGVkX3NucHMpICU+JSAKICAgIGRwbHlyOjpzZWxlY3QoUkFORE9NX1NOUCkgJT4lIAogICAgdW5pcXVlKC4pCiAgIyB3cml0ZSB0byBmaWxlCiAgb3V0X3BhdGggPSBmaWxlLnBhdGgob3V0X2RpciwgcGFzdGUodHJhaXQsICIubGlzdCIsIHNlcCA9ICIiKSkKICByZWFkcjo6d3JpdGVfbGluZXMoc25wcyRSQU5ET01fU05QLCBvdXRfcGF0aCkKfSkKcm0ob3V0KQpgYGAKCiMjIE1ha2UgVkNGcyBmb3IgcmFuZG9tIGNvbnRyb2wgU05QcwoKYGBge2Jhc2gsIGV2YWwgPSBGfQojIE9uIGNsdXN0ZXIKCiMgU2V0IHZhcmlhYmxlcwp0cmFpdHM9JChlY2hvIGhlaSBibWkgZWR1IGludCBpYmQgcGlnKQppbl9kaXJfc25wX2xpc3Q9ZGF0YS8yMDIxMDEyOF9yYW5kb21fc25wcwppbl92Y2Y9Li4vdmNmcy8xa2dfYWxsLnZjZi5negpyZWY9Li4vcmVmcy9oczM3ZDUuZmEuZ3oKb3V0X2Rpcl92Y2ZzPWRhdGEvMjAyMTAxMjhfcm5kbV92Y2ZzCgpta2RpciAtcCAkb3V0X2Rpcl92Y2ZzCgojIE1ha2UgbmV3IFZDRnMKZm9yIHRyYWl0IGluICQoZWNobyAkdHJhaXRzICk7IGRvCiAgYnN1YiBcCiAgICAtTSAyMDAwMCBcCiAgICAtbyAuLi9sb2cvMjAyMTAxMjhfZmlsdGVyX3ZjZnNfcm5kbV8kdHJhaXQub3V0IFwKICAgIC1lIC4uL2xvZy8yMDIxMDEyOF9maWx0ZXJfdmNmc19ybmRtXyR0cmFpdC5lcnIgXAogICAgIiIiCiAgICBjb25kYSBhY3RpdmF0ZSBmc3RfZW52X3JoZWwgOwogICAgZ2F0ayBTZWxlY3RWYXJpYW50cyBcCiAgICAgIC1SICRyZWYgXAogICAgICAtViAkaW5fdmNmIFwKICAgICAgLS1rZWVwLWlkcyAkaW5fZGlyX3NucF9saXN0LyR0cmFpdC5saXN0IFwKICAgICAgLU8gJG91dF9kaXJfdmNmcy8kdHJhaXQudmNmLmd6ICAgICAKICAgICIiIiA7CmRvbmUgICAgCmBgYAoKCiMjIENvbnNvbGlkYXRlIGtleSBkYXRhIGludG8gc2luZ2xlIERGIGZvciBhbmFseXNpcwoKKipOT1RFKio6IGBjbHVtcF9wYXJhbWAgaXMgc2V0IGluIGBjb2RlL3NjcmlwdHMvc291cmNlLlJgCgpgYGB7cn0KZGF0YV9saXN0ID0gbGFwcGx5KGRhdGFfbGlzdCwgZnVuY3Rpb24ocGhlbm8pewogICMgRmlsdGVyIGBjb25zb2xgIGJ5IHRoZSBpbmRleCBTTlBzIGluIHRhcmdldCBgY2x1bXBgCiAgdGFyZ2V0X2NsdW1wID0gcGhlbm9bWyJjbHVtcGVkIl1dW1tjbHVtcF9wYXJhbV1dCiAgCiAgZmluYWwgPSBwaGVub1tbImNvbnNvbCJdXSAlPiUgCiAgICBkcGx5cjo6cmVuYW1lKFNOUCA9IFRPUF9TTlApICU+JSAKICAgIGRwbHlyOjpmaWx0ZXIoU05QICVpbiUgdGFyZ2V0X2NsdW1wJFNOUCkgCiAgCiAgIyBBZGQgYWxsZWxlIGZyZXF1ZW5jaWVzIG9mIFNOUCBoaXRzIChnbG9iYWwgYW5kIHBlci1wb3B1bGF0aW9uKQogIAogICMgQWRkIGNvbnRyb2xzCiAgY29udHJvbHMgPSBwaGVub1tbInJhbmRvbV9hZiJdXSAlPiUKICAgICMgZmlsdGVyIGZvciBTTlBzIGluIHRhcmdldF9jbHVtcAogICAgZHBseXI6OmZpbHRlcihUT1BfU05QICVpbiUgdGFyZ2V0X2NsdW1wJFNOUCkgJT4lIAogICAgZHBseXI6OnNlbGVjdCgtYyhUT1BfU05QLCBCSU5fMTAwLCBSQU5ET01fQklOXzEwMCksIAogICAgICAgICAgICAgICAgICBTTlAgPSBSQU5ET01fU05QKSAlPiUgCiAgICBkcGx5cjo6bXV0YXRlKFJJU0tfQUYgPSBBTFRfRlJFUVMpCiAgCiAgZmluYWwgPSBkcGx5cjo6YmluZF9yb3dzKGZpbmFsLCBjb250cm9scykgIAogIHBoZW5vW1siZmluYWwiXV0gPSBmaW5hbAogIAogIHJldHVybihwaGVubykKfSkKCiMgQ3JlYXRlIGZpbmFsIGRmCmZpbmFsX2RmID0gbGFwcGx5KGRhdGFfbGlzdCwgZnVuY3Rpb24ocGhlbm8pewogIG91dCA9IHBoZW5vW1siZmluYWwiXV0KICAKICByZXR1cm4ob3V0KQp9KSAlPiUgCiAgZHBseXI6OmJpbmRfcm93cygpCgojIFNldCBmYWN0b3JzCmZpbmFsX2RmJFBIRU5PIDwtIGZhY3RvcihmaW5hbF9kZiRQSEVOTywgbGV2ZWxzID0gdHJhaXRfbGV2ZWxzX3ZlcmIpCmZpbmFsX2RmJEhJVF9DT05UUk9MID0gZmFjdG9yKGZpbmFsX2RmJEhJVF9DT05UUk9MLCBsZXZlbHMgPSBoaXRfY29udHJvbF9sZXZlbHMpCgojIENyZWF0ZSBERiBmb3IgcGxvdHRpbmcKZmluYWxfcGx0ID0gZmluYWxfZGYgJT4lIAogIGRwbHlyOjpzZWxlY3QoU05QLCBQSEVOTywgUE9QTiwgUklTS19BRiwgSElUX0NPTlRST0wpICU+JSAKICB0aWR5cjo6cGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IFBPUE4sIHZhbHVlc19mcm9tID0gUklTS19BRikgCgpgYGAKCgojIEFuYWx5c2lzCgojIyBNYW5oYXR0YW4gcGxvdHMKCmBgYHtyLCBtZXNzYWdlID0gRiwgcmVzdWx0cyA9ICJoaWRlIn0KY291bnRlciA9IDAKbGFwcGx5KHVuaXF1ZShmaW5hbF9kZiRQSEVOTyksIGZ1bmN0aW9uKHBoZW5vKXsKICAjIHNldCBjb3VudGVyCiAgY291bnRlciA8PC0gY291bnRlciArIDEKICBkZiA9IGZpbmFsX2RmICU+JSAKICAgIGRwbHlyOjpmaWx0ZXIoUEhFTk8gPT0gcGhlbm8gJiBISVRfQ09OVFJPTCA9PSAiaGl0IikKICAjIEdldCBudW1iZXIgb2YgU05QcwogIHNucF9uID0gbGVuZ3RoKHVuaXF1ZShkZiRTTlApKQogICMgR2V0IHRpdGxlCiAgdGl0bGUgPC0gcGFzdGUocGhlbm8sICJcbiIsICJTTlAgY291bnQ6Iiwgc25wX24pCiAgIyBQbG90CiAgZ2V0X21hbihkZiwgdHJhaXQgPSBwaGVubywgdGl0bGUgPSB0aXRsZSwgY2hyID0gIkNIUiIsIGJwID0gIlBPUyIsIHNucCA9ICJTTlAiLCBwID0gIlAiKQp9KQpgYGAKCiMjIEFsbGVsZSBmcmVxdWVuY3kgZGlzdHJpYnV0aW9ucyBvZiByaXNrIGFsbGVsZXMKCmBgYHtyfQpmaW5hbF9kZiAlPiUKICBkcGx5cjo6ZmlsdGVyKEhJVF9DT05UUk9MID09ICJoaXQiKSAlPiUgCiAgZ2dwbG90KGFlcyhSSVNLX0FGLCBmaWxsID0gUEhFTk8pKSArCiAgICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTAwKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxfcHJpbWFyeSkgKwogICAgZmFjZXRfd3JhcCh2YXJzKFBIRU5PKSwgbnJvdyA9IDIpICsKICAgIGd1aWRlcyhmaWxsID0gRikgKwogICAgeGxhYigiUmlzayBhbGxlbGUgZnJlcXVlbmN5IikgKwogICAgeWxhYigiQ291bnQiKSArCiAgICB0aGVtZV9idygpICsKICAgIGdndGl0bGUoIkZyZXF1ZW5jeSBkaXN0cmlidXRpb24gb2YgXCJyaXNrXCIgYWxsZWxlcyAoYWxsIDFLRyBwb3B1bGF0aW9ucyBjb21iaW5lZCkiKQoKCmBgYApgYGB7ciwgZXZhbCA9IEZ9Cmdnc2F2ZShoZXJlKCJwbG90cyIsICIyMDIxMDEyN19hZl9kaXN0cmlidXRpb24iLCAiMjAyMTAxMjdfaGl0c19hbGwucG5nIiksCiAgICAgICBkZXZpY2UgPSAicG5nIiwKICAgICAgIHVuaXRzID0gImNtIiwKICAgICAgIGRwaSA9IDQwMCwKICAgICAgIGhlaWdodCA9IDEyLAogICAgICAgd2lkdGggPSAyMCkKYGBgCgojIyBBbGxlbGUgZnJlcXVlbmN5IHZzIGVmZmVjdCBzaXplCgpgYGB7ciwgZmlnLnNob3c9ImhvbGQiLCBvdXQud2lkdGg9JzUwJScsIGZpZy5jYXAgPSAiRnVsbCBzaXplIGFuZCB6b29tZWQgdG8gMCA8IHkgPCAyMCJ9Cm9uZSA9IGZpbmFsX2RmICU+JSAKICBkcGx5cjo6ZmlsdGVyKEhJVF9DT05UUk9MID09ICJoaXQiICYgUE9QTiA9PSAiYWxsIikgJT4lIAogIGdncGxvdCgpICsKICAgIGdlb21fcG9pbnQoYWVzKFJJU0tfQUYsIE9SX09SX0JFVEEsIGNvbG91ciA9IFBIRU5PKSwKICAgICAgICAgICAgICAgYWxwaGEgPSAwLjIpICsKICAgIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsX3ByaW1hcnkpICsKICAgIGZhY2V0X3dyYXAodmFycyhQSEVOTyksIG5yb3cgPSAyKSArCiAgICBndWlkZXMoY29sb3VyID0gRiwgYWxwaGEgPSBGKSArCiAgICB0aGVtZV9idygpICsKICAgIGdndGl0bGUoIlJpc2sgYWxsZWxlIGZyZXF1ZW5jeSB2cyBlZmZlY3Qgc2l6ZSAoT1Igb3IgYmV0YSkiKQoKIyBab29tIGluCnR3byA9IGZpbmFsX2RmICU+JSAKICBkcGx5cjo6ZmlsdGVyKEhJVF9DT05UUk9MID09ICJoaXQiICYgUE9QTiA9PSAiYWxsIikgJT4lIAogIGdncGxvdCgpICsKICAgIGdlb21fcG9pbnQoYWVzKFJJU0tfQUYsIE9SX09SX0JFVEEsIGNvbG91ciA9IFBIRU5PKSwKICAgICAgICAgICAgICAgYWxwaGEgPSAwLjIpICsKICAgIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsX3ByaW1hcnkpICsKICAgIGZhY2V0X3dyYXAodmFycyhQSEVOTyksIG5yb3cgPSAyKSArCiAgICBndWlkZXMoY29sb3VyID0gRiwgYWxwaGEgPSBGKSArCiAgICB0aGVtZV9idygpICsKICAgIGdndGl0bGUoIlJpc2sgYWxsZWxlIGZyZXF1ZW5jeSB2cyBlZmZlY3Qgc2l6ZSAoT1Igb3IgYmV0YSkiKSArCiAgICB5bGltKDAsMjApCgpvbmUKdHdvCmBgYAoKYGBge3IsIGV2YWwgPSBGLCBlY2hvID0gRn0Kb25lCmBgYAoKYGBge3IsIGV2YWwgPSBGfQpnZ3NhdmUoaGVyZSgicGxvdHMiLCAiMjAyMTAxMjdfYWZfdl9lZmZlY3Rfc2l6ZSIsICIyMDIxMDEyN19oaXRzX2FsbC5wbmciKSwKICAgICAgIGRldmljZSA9ICJwbmciLAogICAgICAgdW5pdHMgPSAiY20iLAogICAgICAgZHBpID0gNDAwLAogICAgICAgaGVpZ2h0ID0gMTIsCiAgICAgICB3aWR0aCA9IDIwKQpgYGAKCmBgYHtyLCBldmFsID0gRiwgZWNobyA9IEZ9CnR3bwpgYGAKCmBgYHtyLCBldmFsID0gRn0KZ2dzYXZlKGhlcmUoInBsb3RzIiwgIjIwMjEwMTI3X2FmX3ZfZWZmZWN0X3NpemUiLCAiMjAyMTAxMjdfaGl0c196b29tZWQucG5nIiksCiAgICAgICBkZXZpY2UgPSAicG5nIiwKICAgICAgIHVuaXRzID0gImNtIiwKICAgICAgIGRwaSA9IDQwMCwKICAgICAgIGhlaWdodCA9IDEyLAogICAgICAgd2lkdGggPSAyMCkKYGBgCgojIyBGc3QKCiMjIyBXaXRoIGFsbCBwb3B1bGF0aW9ucwoKIyMjIyBHZXQgRnN0IHN0YXRzIGZvciB0b3AgU05QcwoKYGBge3IsIG1lc3NhZ2U9Riwgd2FybmluZz1GLCByZXN1bHRzPUYsIGV2YWwgPSBGfQojIENyZWF0ZSByYXcgbGlzdCBvZiB2YXJpYW50cwpoaXQgPSBoZXJlOjpoZXJlKCJkYXRhIiwgIjIwMjEwMTI4X2hpdHNfdmNmcyIpCmNvbnRyb2wgPSBoZXJlOjpoZXJlKCJkYXRhIiwgIjIwMjEwMTI4X3JuZG1fdmNmcyIpCnRhcmdldF9kaXJzID0gYyhoaXQsIGNvbnRyb2wpCm5hbWVzKHRhcmdldF9kaXJzKSA9IGMoImhpdCIsICJjb250cm9sIikKCiMgU2V0IHNlZWQKI2luaXRpYWxfc2VlZCA9IDUzCiNzZXQuc2VlZChpbml0aWFsX3NlZWQpCiNzZWVkcyA9IHNhbXBsZSgxOjEwMDAsIHNpemUgPSBsZW5ndGgodGFyZ2V0X2RpcnMpKQoKIyBSdW4gCmNvdW50ZXIgPSAwCmZzdF9vdXQgPSBsYXBwbHkodGFyZ2V0X2RpcnMsIGZ1bmN0aW9uKHRhcmdldF9kaXIpewogICMgc2V0IGNvdW50ZXIgCiAgY291bnRlciA8PC0gY291bnRlciArIDEKICAKICB2Y2ZfbGlzdF9yYXcgPC0gbGFwcGx5KHRyYWl0X2xldmVscywgZnVuY3Rpb24odHJhaXQpewogICAgdGFyZ2V0X2ZpbGUgPSBmaWxlLnBhdGgodGFyZ2V0X2RpciwgcGFzdGUodHJhaXQsICIudmNmLmd6Iiwgc2VwID0gIiIpKQogICAgIyByZWFkIGluIGZpbGUKICAgIHZjZl9vdXQgPC0gcGVnYXM6OnJlYWQudmNmKHRhcmdldF9maWxlKQogICAgCiAgICByZXR1cm4odmNmX291dCkKICB9KQogIAogICMgQ3JlYXRlIHZlY3RvciBvZiBwb3B1bGF0aW9ucwogIHBvcHVsYXRpb25zIDwtIHVubGlzdChsYXBwbHkocm93bmFtZXModmNmX2xpc3RfcmF3W1sxXV0pLCBmdW5jdGlvbihzYW1wbGUpewogICAgbWV0YSRQb3B1bGF0aW9uW21ldGEkU2FtcGxlID09IHNhbXBsZV0KICB9KSkKICAKICAjIEdlbmVyYXRlIEZzdCBzdGF0cwogIGZzdF9vdXRfZGYgPC0gbGFwcGx5KHZjZl9saXN0X3JhdywgZnVuY3Rpb24ocGhlbm8pewogICAgb3V0ID0gYXMuZGF0YS5mcmFtZShwZWdhczo6RnN0KHBoZW5vLCBwb3AgPSBwb3B1bGF0aW9ucykpCiAgICAjIHB1dCByb3duYW1lcyBpbnRvIHNlcGFyYXRlIGNvbHVtbgogICAgb3V0JHNucCA8LSByb3duYW1lcyhvdXQpCiAgICAKICAgIHJldHVybihvdXQpCiAgfSkgJT4lIAogICAgIyBiaW5kIGludG8gc2luZ2xlIERGCiAgICBkcGx5cjo6YmluZF9yb3dzKC5pZCA9ICJwaGVub3R5cGUiKSAlPiUgCiAgICAjIHJlbW92ZSBOQQogICAgdGlkeXI6OmRyb3BfbmEoKQogIAogIHJldHVybihmc3Rfb3V0X2RmKQp9KSAlPiUgCiAgZHBseXI6OmJpbmRfcm93cyguaWQgPSAiaGl0X2NvbnRyb2wiKQoKIyBSZWNvZGUgcGhlbm90eXBlCmZzdF9vdXQkcGhlbm90eXBlIDwtIGZhY3Rvcihmc3Rfb3V0JHBoZW5vdHlwZSwgbGV2ZWxzID0gdHJhaXRfbGV2ZWxzKQpmc3Rfb3V0JHBoZW5vdHlwZSA9IGRwbHlyOjpyZWNvZGUoZnN0X291dCRwaGVub3R5cGUsICEhIXJlY29kZV92ZWMpCmBgYAoKV3JpdGUgdG8gZmlsZQoKYGBge3IsIGV2YWwgPSBGfQpvdXRfZGlyID0gaGVyZTo6aGVyZSgiZGF0YSIsICIyMDIxMDEyN19yZXN1bHRzIikKb3V0X3BhdGggPSBmaWxlLnBhdGgob3V0X2RpciwgcGFzdGUoIjIwMjEwMTI4X2ZzdCIsICIuY3N2Iiwgc2VwID0gIiIpKQoKZGlyLmNyZWF0ZShvdXRfZGlyKQoKcmVhZHI6OndyaXRlX2Nzdihmc3Rfb3V0LCBvdXRfcGF0aCkKYGBgCgpSZWFkIGJhY2sgaW4gCmBgYHtyLCBtZXNzYWdlID0gRiwgcmVzdWx0cyA9ICdhc2lzJ30KZnN0X291dCA9IHJlYWRyOjpyZWFkX2NzdihoZXJlOjpoZXJlKCJkYXRhIiwgIjIwMjEwMTI3X3Jlc3VsdHMvMjAyMTAxMjhfZnN0LmNzdiIpKQpmc3Rfb3V0JHBoZW5vdHlwZSA8LSBmYWN0b3IoZnN0X291dCRwaGVub3R5cGUsIGxldmVscyA9IHRyYWl0X2xldmVsc192ZXJiKQoKa25pdHI6OmthYmxlKGhlYWQoZnN0X291dCkpCmBgYAoKIyMjIyAqRnN0KiBoaXN0b2dyYW1zCgpgYGB7cixmaWcuc2hvdz0iaG9sZCIsIG91dC53aWR0aD0nNTAlJywgZmlnLmNhcCA9ICJIaXRzIHZzIGNvbnRyb2xzIn0Kb25lID0gZnN0X291dCAlPiUKICBkcGx5cjo6ZmlsdGVyKGhpdF9jb250cm9sID09ICJoaXQiKSAlPiUgCiAgZ2dwbG90KCkgKwogICAgZ2VvbV9oaXN0b2dyYW0oYWVzKEZzdCwgZmlsbCA9IHBoZW5vdHlwZSksIGJpbnMgPSAxMDApICsKICAgIGZhY2V0X3dyYXAofnBoZW5vdHlwZSkgKwogICAgdGhlbWVfYncoKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxfcHJpbWFyeSkgICsKICAgIGd1aWRlcyhmaWxsID0gRikKCnR3byA9IGZzdF9vdXQgJT4lCiAgZHBseXI6OmZpbHRlcihoaXRfY29udHJvbCA9PSAiY29udHJvbCIpICU+JSAKICBnZ3Bsb3QoKSArCiAgICBnZW9tX2hpc3RvZ3JhbShhZXMoRnN0LCBmaWxsID0gcGhlbm90eXBlKSwgYmlucyA9IDEwMCkgKwogICAgZmFjZXRfd3JhcCh+cGhlbm90eXBlKSArCiAgICB0aGVtZV9idygpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbF9zZWNvbmRhcnkpICsKICAgIGd1aWRlcyhmaWxsID0gRikKCm9uZQp0d28KYGBgCgpgYGB7ciwgZXZhbCA9IEYsIGVjaG8gID0gRn0Kb25lCmBgYAoKYGBge3IsIGV2YWwgPSBGfQpnZ3NhdmUoaGVyZSgicGxvdHMiLCAiMjAyMTAxMjdfaGlzdG9ncmFtcyIsICIyMDIxMDEyOF9oaXRzX2FsbC5wbmciKSwKICAgICAgIGRldmljZSA9ICJwbmciLAogICAgICAgdW5pdHMgPSAiY20iLAogICAgICAgZHBpID0gNDAwLAogICAgICAgaGVpZ2h0ID0gMTIsCiAgICAgICB3aWR0aCA9IDIwKQpgYGAKCmBgYHtyLCBldmFsID0gRiwgZWNobyAgPSBGfQp0d28KYGBgCgpgYGB7ciwgZXZhbCA9IEZ9Cmdnc2F2ZShoZXJlKCJwbG90cyIsICIyMDIxMDEyN19oaXN0b2dyYW1zIiwgIjIwMjEwMTI4X2NvbnRyb2xzX2FsbC5wbmciKSwKICAgICAgIGRldmljZSA9ICJwbmciLAogICAgICAgdW5pdHMgPSAiY20iLAogICAgICAgZHBpID0gNDAwLAogICAgICAgaGVpZ2h0ID0gMTIsCiAgICAgICB3aWR0aCA9IDIwKQpgYGAKCiMjIyMgKkZzdCogZGVuc2l0eQoKIyMjIyMgRmFjZXRzCgpgYGB7ciwgd2FybmluZz0gRiwgZmlnLnNob3c9ImhvbGQiLCBvdXQud2lkdGg9JzUwJScsIGZpZy5jYXAgPSAiSGl0cyB2cyBjb250cm9scyJ9Cm9uZSA9IGZzdF9vdXQgJT4lIAogIGRwbHlyOjpmaWx0ZXIoaGl0X2NvbnRyb2wgPT0gImhpdCIpICU+JSAKICBnZ3Bsb3QoYWVzKEZzdCwgZmlsbCA9IHBoZW5vdHlwZSkpICsKICAgIGdlb21fZGVuc2l0eSgpICsKICAgIGxhYnMoZmlsbCA9ICJQaGVub3R5cGUiKSArCiAgICBmYWNldF93cmFwKH5waGVub3R5cGUpICsKICAgIHlsYWIoIkRlbnNpdHkiKSArCiAgICB0aGVtZV9idygpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbF9wcmltYXJ5KSArCiAgICBndWlkZXMoZmlsbCA9IEYpCgoKdHdvID0gZnN0X291dCAlPiUgCiAgZHBseXI6OmZpbHRlcihoaXRfY29udHJvbCA9PSAiY29udHJvbCIpICU+JSAKICBnZ3Bsb3QoYWVzKEZzdCwgZmlsbCA9IHBoZW5vdHlwZSkpICsKICAgIGdlb21fZGVuc2l0eSgpICsKICAgIGxhYnMoZmlsbCA9ICJQaGVub3R5cGUiKSArCiAgICBmYWNldF93cmFwKH5waGVub3R5cGUpICsKICAgIHlsYWIoIkRlbnNpdHkiKSArCiAgICB0aGVtZV9idygpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbF9zZWNvbmRhcnkpICsKICAgIGd1aWRlcyhmaWxsID0gRikKCm9uZQp0d28KYGBgCgpgYGB7ciwgZXZhbCA9IEYsIGVjaG8gPSBGfQpvbmUKYGBgCgpgYGB7ciwgZXZhbCA9IEZ9Cmdnc2F2ZShoZXJlKCJwbG90cyIsICIyMDIxMDEyN19kZW5zaXRpZXMiLCAiMjAyMTAxMjhfaGl0c19hbGwucG5nIiksCiAgICAgICBkZXZpY2UgPSAicG5nIiwKICAgICAgIHVuaXRzID0gImNtIiwKICAgICAgIGRwaSA9IDQwMCwKICAgICAgIGhlaWdodCA9IDEyLAogICAgICAgd2lkdGggPSAyMCkKYGBgCgpgYGB7ciwgZXZhbCA9IEYsIGVjaG8gPSBGfQp0d28KYGBgCgpgYGB7ciwgZXZhbCA9IEZ9Cmdnc2F2ZShoZXJlKCJwbG90cyIsICIyMDIxMDEyN19kZW5zaXRpZXMiLCAiMjAyMTAxMjhfY29udHJvbHNfYWxsLnBuZyIpLAogICAgICAgZGV2aWNlID0gInBuZyIsCiAgICAgICB1bml0cyA9ICJjbSIsCiAgICAgICBkcGkgPSA0MDAsCiAgICAgICBoZWlnaHQgPSAxMiwKICAgICAgIHdpZHRoID0gMjApCmBgYAoKIyMjIyMgUmlkZ2VzCgpgYGB7ciwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGLCBmaWcuc2hvdz0iaG9sZCIsIG91dC53aWR0aD0nNTAlJywgZmlnLmNhcCA9ICJIaXRzIHZzIGNvbnRyb2xzIn0Kb25lID0gZnN0X291dCAlPiUgCiAgZHBseXI6OmZpbHRlcihoaXRfY29udHJvbCA9PSAiaGl0IikgJT4lIAogIGdncGxvdCgpICsKICAgIGdlb21fZGVuc2l0eV9yaWRnZXMyKG1hcHBpbmcgPSBhZXMoeCA9IEZzdCwgeSA9IHBoZW5vdHlwZSwgZmlsbCA9IHBoZW5vdHlwZSksCiAgICAgICAgICAgICAgICAgICAgICAgICBzY2FsZSA9IDIpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbF9wcmltYXJ5KSArCiAgICB5bGFiKGxhYmVsID0gTlVMTCkgKwogICAgdGhlbWVfYncoKSArCiAgICBndWlkZXMoZmlsbCA9IEYpICsKICAgIHNjYWxlX3lfZGlzY3JldGUoZXhwYW5kID0gZXhwYW5kX3NjYWxlKGFkZCA9IGMoMC4yLCAyLjMpKSkKCnR3byA9IGZzdF9vdXQgJT4lCiAgZHBseXI6OmZpbHRlcihoaXRfY29udHJvbCA9PSAiY29udHJvbCIpICU+JSAKICBnZ3Bsb3QoKSArCiAgICBnZW9tX2RlbnNpdHlfcmlkZ2VzMihtYXBwaW5nID0gYWVzKHggPSBGc3QsIHkgPSBwaGVub3R5cGUsIGZpbGwgPSBwaGVub3R5cGUpLAogICAgICAgICAgICAgICAgICAgICAgICAgc2NhbGUgPSAyKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxfc2Vjb25kYXJ5KSArCiAgICB5bGFiKGxhYmVsID0gTlVMTCkgKwogICAgdGhlbWVfYncoKSArCiAgICBndWlkZXMoZmlsbCA9IEYpICsKICAgIHNjYWxlX3lfZGlzY3JldGUoZXhwYW5kID0gZXhwYW5kX3NjYWxlKGFkZCA9IGMoMC4yLCAyLjMpKSkKCm9uZQp0d28KYGBgCgpgYGB7ciwgZXZhbCA9IEYsIGVjaG8gPSBGfQpvbmUKYGBgCgpgYGB7ciwgZXZhbCA9IEZ9Cmdnc2F2ZShoZXJlKCJwbG90cyIsICIyMDIxMDEyN19yaWRnZXMiLCAiMjAyMTAxMjhfaGl0c19hbGwucG5nIiksCiAgICAgICBkZXZpY2UgPSAicG5nIiwKICAgICAgIHVuaXRzID0gImNtIiwKICAgICAgIGRwaSA9IDQwMCwKICAgICAgIGhlaWdodCA9IDEyLAogICAgICAgd2lkdGggPSAyMCkKYGBgCgpgYGB7ciwgZXZhbCA9IEYsIGVjaG8gPSBGfQp0d28KYGBgCgpgYGB7ciwgZXZhbCA9IEZ9Cmdnc2F2ZShoZXJlKCJwbG90cyIsICIyMDIxMDEyN19yaWRnZXMiLCAiMjAyMTAxMjhfY29udHJvbHNfYWxsLnBuZyIpLAogICAgICAgZGV2aWNlID0gInBuZyIsCiAgICAgICB1bml0cyA9ICJjbSIsCiAgICAgICBkcGkgPSA0MDAsCiAgICAgICBoZWlnaHQgPSAxMiwKICAgICAgIHdpZHRoID0gMjApCmBgYAoKIyMjIFJ1biBLb2xtb2dvcm92LVNtaXJub3YgVGVzdHMKCmBgYHtyLCB3YXJuaW5nID0gRn0KaGl0X2NvbnRyb2wgPSB1bmlxdWUoZnN0X291dCRoaXRfY29udHJvbCkKbmFtZXMoaGl0X2NvbnRyb2wpID0gaGl0X2NvbnRyb2wKCmtzX291dCA9IGxhcHBseShoaXRfY29udHJvbCwgZnVuY3Rpb24oZGF0YXNldCl7CiAgIyBmaWx0ZXIgZGF0YXNldAogIHRhcmdldF9kZiA9IGZzdF9vdXRbZnN0X291dCRoaXRfY29udHJvbCA9PSBkYXRhc2V0LCBdCiAgIyBydW4gcGFpcndpc2UgS1MgdGVzdHMKICBrc19vdXQgPSBsYXBwbHkodHJhaXRfbGV2ZWxzX3ZlcmIsIGZ1bmN0aW9uKHRyYWl0X2EpewogICAgb3V0ID0gbGFwcGx5KHRyYWl0X2xldmVsc192ZXJiLCBmdW5jdGlvbih0cmFpdF9iKXsKICAgICAgcmVzdWx0cyA9IGtzLnRlc3QodGFyZ2V0X2RmJEZzdFt0YXJnZXRfZGYkcGhlbm90eXBlID09IHRyYWl0X2FdLAogICAgICAgICAgICAgICAgICAgICAgICB0YXJnZXRfZGYkRnN0W3RhcmdldF9kZiRwaGVub3R5cGUgPT0gdHJhaXRfYl0pCiAgICAgIFAgPSByZXN1bHRzJHAudmFsdWUKICAgICAgCiAgICAgIHJldHVybihQKQogICAgfSkgJT4lIAogICAgICBkcGx5cjo6YmluZF9yb3dzKC5pZCA9ICJ0ZXN0X2IiKQogICAgCiAgICByZXR1cm4ob3V0KQogIH0pICAlPiUgCiAgICBkcGx5cjo6YmluZF9yb3dzKC5pZCA9ICJ0cmFpdCIpCiAgCiAgdHJhaXRzID0ga3Nfb3V0JHRyYWl0CiAga3Nfb3V0JHRyYWl0IDwtIE5VTEwKICAKICByb3duYW1lcyhrc19vdXQpID0gdHJhaXRzCiAgcmV0dXJuKGtzX291dCkKfSkKCiMgY29udmVydCB0byBtYXRyaXgKa3NfbWF0ID0gbGFwcGx5KGtzX291dCwgZnVuY3Rpb24oZGF0YXNldCl7CiAgb3V0ID0gYXMubWF0cml4KGRhdGFzZXQpCiAgCiAgcmV0dXJuKG91dCkKfSkKYGBgCgoKYGBge3J9CiMgUHJvY2VzcyBmb3IgcGxvdHRpbmcKa3Nfb3V0X2dnID0gbGFwcGx5KGtzX291dCwgZnVuY3Rpb24oZGF0YXNldCl7CiAgb3V0ID0gZGF0YXNldAogIG91dCRBID0gcm93bmFtZXMoZGF0YXNldCkKICBvdXQgPSBvdXQgJT4lIAogICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAtQSwgbmFtZXNfdG8gPSAiQiIsIHZhbHVlc190byA9ICJrc19QIikKICAjIGNvbnZlcnQgUC12YWx1ZXMgdG8gLWxvZzEwCiAgb3V0JGtzX1AgPSAtbG9nMTAob3V0JGtzX1ApCiAgCiAgcmV0dXJuKG91dCkKfSkgJT4lIAogIGRwbHlyOjpiaW5kX3Jvd3MoLmlkID0gImhpdF9jb250cm9sIikgJT4lIAogIGRwbHlyOjptdXRhdGUoYWNyb3NzKGMoIkEiLCAiQiIpLAogICAgICAgICAgICAgICAgICAgICAgIH5mYWN0b3IoLngsIGxldmVscyA9IHRyYWl0X2xldmVsc192ZXJiKSkpCmBgYAoKUGxvdApgYGB7ciwgZmlnLnNob3c9ImhvbGQiLCBvdXQud2lkdGg9JzUwJScsIGZpZy5jYXAgPSAiSGl0cyB2cyBjb250cm9scyJ9CmhlYXRfaGl0c19hbGwgPSBrc19vdXRfZ2cgJT4lIAogIGRwbHlyOjpmaWx0ZXIoaGl0X2NvbnRyb2wgPT0gImhpdCIpICU+JSAKICBnZ3Bsb3QoKSArCiAgICBnZW9tX3RpbGUoYWVzKEEsIEIsIGZpbGwgPSBrc19QKSkgKwogICAgc2NhbGVfZmlsbF92aXJpZGlzX2MoKSArCiAgICBjb29yZF9maXhlZCgpICsKICAgIHhsYWIoTlVMTCkgKwogICAgeWxhYihOVUxMKSArCiAgICBsYWJzKGZpbGwgPSAiS1MtdGVzdFxuLWxvZyhQKSIpCgpoZWF0X2NvbnRyb2xzX2FsbCA9IGtzX291dF9nZyAlPiUgCiAgZHBseXI6OmZpbHRlcihoaXRfY29udHJvbCA9PSAiY29udHJvbCIpICU+JSAKICBnZ3Bsb3QoKSArCiAgICBnZW9tX3RpbGUoYWVzKEEsIEIsIGZpbGwgPSBrc19QKSkgKwogICAgc2NhbGVfZmlsbF92aXJpZGlzX2Mob3B0aW9uID0gIm1hZ21hIikgKwogICAgY29vcmRfZml4ZWQoKSArCiAgICB4bGFiKE5VTEwpICsKICAgIHlsYWIoTlVMTCkgKwogICAgbGFicyhmaWxsID0gIktTLXRlc3Rcbi1sb2coUCkiKQoKaGVhdF9oaXRzX2FsbApoZWF0X2NvbnRyb2xzX2FsbApgYGAKCmBgYHtyLCBldmFsID0gRiwgZWNobyA9IEZ9CmhlYXRfaGl0c19hbGwKYGBgCgpgYGB7ciwgZXZhbCA9IEZ9Cmdnc2F2ZShoZXJlKCJwbG90cyIsICIyMDIxMDEyN19rc19oZWF0bWFwcyIsICIyMDIxMDEyN19oaXRzX2FsbC5wbmciKSwKICAgICAgIGRldmljZSA9ICJwbmciLAogICAgICAgdW5pdHMgPSAiY20iLAogICAgICAgZHBpID0gNDAwLAogICAgICAgaGVpZ2h0ID0gMTIsCiAgICAgICB3aWR0aCA9IDIwKQpgYGAKCmBgYHtyLCBldmFsID0gRiwgZWNobyA9IEZ9CmhlYXRfY29udHJvbHNfYWxsCmBgYAoKYGBge3IsIGV2YWwgPSBGfQpnZ3NhdmUoaGVyZSgicGxvdHMiLCAiMjAyMTAxMjdfa3NfaGVhdG1hcHMiLCAiMjAyMTAxMjdfY29udHJvbHNfYWxsLnBuZyIpLAogICAgICAgZGV2aWNlID0gInBuZyIsCiAgICAgICB1bml0cyA9ICJjbSIsCiAgICAgICBkcGkgPSA0MDAsCiAgICAgICBoZWlnaHQgPSAxMiwKICAgICAgIHdpZHRoID0gMjApCmBgYAoKIyMjIFRyeSB3aXRoIHNhbWUgbnVtYmVyIG9mIFNOUHMgCgpbKipTdHJhdGVneSoqXXtjb2xvcj0icmVkIn06IFdpbGwgaXQgbWFrZSBhIGRpZmZlcmVuY2UgaWYgd2UgYW5hbHlzZSB0aGUgc2FtZSBudW1iZXIgb2YgU05QcyBmb3IgZWFjaCB0cmFpdD8gaS5lLiBpcyB0aGUgZGlmZmVyZW50IG51bWJlciBvZiBTTlBzIGZvciBlYWNoIHRyYWl0IGFmZmVjdGluZyB0aGUgS1MgdGVzdCBvdXRwdXQ/CgpJQkQgb25seSBoYXMgMTgyIGhpdHMsIHNvIHJlbW92ZSBmcm9tIHRoaXMgcGFydCBvZiB0aGUgYW5hbHlzaXMuCgpUaGUgdHJhaXQgd2l0aCB0aGUgbmV4dCBsZWFzdCBudW1iZXIgb2YgaGl0cyBpcyBgSW50ZWxsaWdlbmNlYCwgd2l0aCBgNDgwLiBTbyB3ZSdsbCB0YWtlIGEgcmFuZG9tIHNhbXBsZSBvZiA0MAoKYGBge3J9CmZzdF9zYW1wbGUgPSBzcGxpdChmc3Rfb3V0LCBmID0gZnN0X291dCRoaXRfY29udHJvbCkKZnN0X3NhbXBsZSA9IGxhcHBseShmc3Rfc2FtcGxlLCBmdW5jdGlvbihkYXRhc2V0KXsKICBvdXQgPSBzcGxpdChkYXRhc2V0LCBmID0gZGF0YXNldCRwaGVub3R5cGUpCiAgb3V0ID0gbGFwcGx5KG91dCwgZnVuY3Rpb24ocGhlbm8pewogICAgcGhlbm8gPSBwaGVubyAlPiUgCiAgICAgIGRwbHlyOjpzbGljZV9zYW1wbGUobiA9IDQ4MCkKICAgIAogICAgcmV0dXJuKHBoZW5vKQogIH0pICU+JSAKICAgIGRwbHlyOjpiaW5kX3Jvd3MoKSAlPiUgCiAgICBkcGx5cjo6ZmlsdGVyKHBoZW5vdHlwZSAhPSAiSUJEIikKfSkgJT4lIAogIGRwbHlyOjpiaW5kX3Jvd3MoKQpgYGAKCiMjIyMgSGlzdG9ncmFtcwoKYGBge3IsIGZpZy5zaG93PSJob2xkIiwgb3V0LndpZHRoPSc1MCUnLCBmaWcuY2FwID0gIlNBTVBMRUQ6IEhpdHMgdnMgY29udHJvbHMifQpvbmUgPSBmc3Rfc2FtcGxlICU+JQogIGRwbHlyOjpmaWx0ZXIoaGl0X2NvbnRyb2wgPT0gImhpdCIpICU+JSAKICBnZ3Bsb3QoKSArCiAgICBnZW9tX2hpc3RvZ3JhbShhZXMoRnN0LCBmaWxsID0gcGhlbm90eXBlKSwgYmlucyA9IDEwMCkgKwogICAgZmFjZXRfd3JhcCh+cGhlbm90eXBlKSArCiAgICB0aGVtZV9idygpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbF9wcmltYXJ5KSAgKwogICAgZ3VpZGVzKGZpbGwgPSBGKQoKdHdvID0gZnN0X3NhbXBsZSAlPiUKICBkcGx5cjo6ZmlsdGVyKGhpdF9jb250cm9sID09ICJjb250cm9sIikgJT4lIAogIGdncGxvdCgpICsKICAgIGdlb21faGlzdG9ncmFtKGFlcyhGc3QsIGZpbGwgPSBwaGVub3R5cGUpLCBiaW5zID0gMTAwKSArCiAgICBmYWNldF93cmFwKH5waGVub3R5cGUpICsKICAgIHRoZW1lX2J3KCkgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsX3NlY29uZGFyeSkgKwogICAgZ3VpZGVzKGZpbGwgPSBGKQoKb25lCnR3bwpgYGAKYGBge3IsIGV2YWwgPSBGLCBlY2hvICA9IEZ9Cm9uZQpgYGAKCmBgYHtyLCBldmFsID0gRn0KZ2dzYXZlKGhlcmUoInBsb3RzIiwgIjIwMjEwMTI3X2hpc3RvZ3JhbXMiLCAiMjAyMTAxMjdfaGl0c19zYW1wbGUucG5nIiksCiAgICAgICBkZXZpY2UgPSAicG5nIiwKICAgICAgIHVuaXRzID0gImNtIiwKICAgICAgIGRwaSA9IDQwMCwKICAgICAgIGhlaWdodCA9IDEyLAogICAgICAgd2lkdGggPSAyMCkKYGBgCgpgYGB7ciwgZXZhbCA9IEYsIGVjaG8gID0gRn0KdHdvCmBgYAoKYGBge3IsIGV2YWwgPSBGfQpnZ3NhdmUoaGVyZSgicGxvdHMiLCAiMjAyMTAxMjdfaGlzdG9ncmFtcyIsICIyMDIxMDEyN19jb250cm9sc19zYW1wbGUucG5nIiksCiAgICAgICBkZXZpY2UgPSAicG5nIiwKICAgICAgIHVuaXRzID0gImNtIiwKICAgICAgIGRwaSA9IDQwMCwKICAgICAgIGhlaWdodCA9IDEyLAogICAgICAgd2lkdGggPSAyMCkKYGBgCgojIyMjIERlbnNpdHkKCmBgYHtyLCB3YXJuaW5nPSBGLCBmaWcuc2hvdz0iaG9sZCIsIG91dC53aWR0aD0nNTAlJywgZmlnLmNhcCA9ICJTQU1QTEVEOiBIaXRzIHZzIGNvbnRyb2xzIn0Kb25lID0gZnN0X3NhbXBsZSAlPiUgCiAgZHBseXI6OmZpbHRlcihoaXRfY29udHJvbCA9PSAiaGl0IikgJT4lIAogIGdncGxvdChhZXMoRnN0LCBmaWxsID0gcGhlbm90eXBlKSkgKwogICAgZ2VvbV9kZW5zaXR5KCkgKwogICAgbGFicyhmaWxsID0gIlBoZW5vdHlwZSIpICsKICAgIGZhY2V0X3dyYXAofnBoZW5vdHlwZSkgKwogICAgeWxhYigiRGVuc2l0eSIpICsKICAgIHRoZW1lX2J3KCkgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsX3ByaW1hcnkpICsKICAgIGd1aWRlcyhmaWxsID0gRikKCnR3byA9IGZzdF9zYW1wbGUgJT4lIAogIGRwbHlyOjpmaWx0ZXIoaGl0X2NvbnRyb2wgPT0gImNvbnRyb2wiKSAlPiUgCiAgZ2dwbG90KGFlcyhGc3QsIGZpbGwgPSBwaGVub3R5cGUpKSArCiAgICBnZW9tX2RlbnNpdHkoKSArCiAgICBsYWJzKGZpbGwgPSAiUGhlbm90eXBlIikgKwogICAgZmFjZXRfd3JhcCh+cGhlbm90eXBlKSArCiAgICB5bGFiKCJEZW5zaXR5IikgKwogICAgdGhlbWVfYncoKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxfc2Vjb25kYXJ5KSArCiAgICBndWlkZXMoZmlsbCA9IEYpCgpvbmUKdHdvCmBgYAoKYGBge3IsIGV2YWwgPSBGLCBlY2hvID0gRn0Kb25lCmBgYAoKYGBge3IsIGV2YWwgPSBGfQpnZ3NhdmUoaGVyZSgicGxvdHMiLCAiMjAyMTAxMjdfZGVuc2l0aWVzIiwgIjIwMjEwMTI3X2hpdHNfc2FtcGxlLnBuZyIpLAogICAgICAgZGV2aWNlID0gInBuZyIsCiAgICAgICB1bml0cyA9ICJjbSIsCiAgICAgICBkcGkgPSA0MDAsCiAgICAgICBoZWlnaHQgPSAxMiwKICAgICAgIHdpZHRoID0gMjApCmBgYAoKYGBge3IsIGV2YWwgPSBGLCBlY2hvID0gRn0KdHdvCmBgYAoKYGBge3IsIGV2YWwgPSBGfQpnZ3NhdmUoaGVyZSgicGxvdHMiLCAiMjAyMTAxMjdfZGVuc2l0aWVzIiwgIjIwMjEwMTI3X2NvbnRyb2xzX3NhbXBsZS5wbmciKSwKICAgICAgIGRldmljZSA9ICJwbmciLAogICAgICAgdW5pdHMgPSAiY20iLAogICAgICAgZHBpID0gNDAwLAogICAgICAgaGVpZ2h0ID0gMTIsCiAgICAgICB3aWR0aCA9IDIwKQpgYGAKCiMjIyBLUyB0ZXN0CgpgYGB7ciwgd2FybmluZyA9IEZ9CmhpdF9jb250cm9sID0gdW5pcXVlKGZzdF9zYW1wbGUkaGl0X2NvbnRyb2wpCm5hbWVzKGhpdF9jb250cm9sKSA9IGhpdF9jb250cm9sCgp0cmFpdF9sZXZlbHNfdmVyYl9zYW1wbGUgPSB0cmFpdF9sZXZlbHNfdmVyYlstd2hpY2godHJhaXRfbGV2ZWxzX3ZlcmIgPT0gIklCRCIpXQp0cmFpdHNfc2FtcGxlID0gdHJhaXRzWy13aGljaCh0cmFpdHMgPT0gImliZCIpXQoKa3Nfc2FtcGxlID0gbGFwcGx5KGhpdF9jb250cm9sLCBmdW5jdGlvbihkYXRhc2V0KXsKICAjIGZpbHRlciBkYXRhc2V0CiAgdGFyZ2V0X2RmID0gZnN0X3NhbXBsZVtmc3Rfc2FtcGxlJGhpdF9jb250cm9sID09IGRhdGFzZXQsIF0KICAjIHJ1biBwYWlyd2lzZSBLUyB0ZXN0cwogIGtzX291dCA9IGxhcHBseSh0cmFpdF9sZXZlbHNfdmVyYl9zYW1wbGUsIGZ1bmN0aW9uKHRyYWl0X2EpewogICAgb3V0ID0gbGFwcGx5KHRyYWl0X2xldmVsc192ZXJiX3NhbXBsZSwgZnVuY3Rpb24odHJhaXRfYil7CiAgICAgIHJlc3VsdHMgPSBrcy50ZXN0KHRhcmdldF9kZiRGc3RbdGFyZ2V0X2RmJHBoZW5vdHlwZSA9PSB0cmFpdF9hXSwKICAgICAgICAgICAgICAgICAgICAgICAgdGFyZ2V0X2RmJEZzdFt0YXJnZXRfZGYkcGhlbm90eXBlID09IHRyYWl0X2JdKQogICAgICBQID0gcmVzdWx0cyRwLnZhbHVlCiAgICAgIAogICAgICByZXR1cm4oUCkKICAgIH0pICU+JSAKICAgICAgZHBseXI6OmJpbmRfcm93cyguaWQgPSAidGVzdF9iIikKICAgIAogICAgcmV0dXJuKG91dCkKICB9KSAgJT4lIAogICAgZHBseXI6OmJpbmRfcm93cyguaWQgPSAidHJhaXQiKQogIAogIHRyYWl0cyA9IGtzX291dCR0cmFpdF9sZXZlbHNfdmVyYl9zYW1wbGUKICBrc19vdXQkdHJhaXQgPC0gTlVMTAogIAogIHJvd25hbWVzKGtzX291dCkgPSB0cmFpdF9sZXZlbHNfdmVyYl9zYW1wbGUKICByZXR1cm4oa3Nfb3V0KQp9KQpgYGAKCiMjIyMgSGVhdG1hcHMKCmBgYHtyfQprc19zYW1wbGVfZ2cgPSBsYXBwbHkoa3Nfc2FtcGxlLCBmdW5jdGlvbihkYXRhc2V0KXsKICBvdXQgPSBkYXRhc2V0CiAgb3V0JEEgPSByb3duYW1lcyhkYXRhc2V0KQogIG91dCA9IG91dCAlPiUgCiAgICBwaXZvdF9sb25nZXIoY29scyA9IC1BLCBuYW1lc190byA9ICJCIiwgdmFsdWVzX3RvID0gImtzX1AiKQogICMgY29udmVydCBQLXZhbHVlcyB0byAtbG9nMTAKICBvdXQka3NfUCA9IC1sb2cxMChvdXQka3NfUCkKICAKICByZXR1cm4ob3V0KQp9KSAlPiUgCiAgZHBseXI6OmJpbmRfcm93cyguaWQgPSAiaGl0X2NvbnRyb2wiKSAlPiUgCiAgZHBseXI6Om11dGF0ZShhY3Jvc3MoYygiQSIsICJCIiksCiAgICAgICAgICAgICAgICAgICAgICAgfmZhY3RvcigueCwgbGV2ZWxzID0gdHJhaXRfbGV2ZWxzX3ZlcmJfc2FtcGxlKSkpCmBgYAoKUGxvdApgYGB7ciwgZmlnLnNob3c9ImhvbGQiLCBvdXQud2lkdGg9JzUwJScsIGZpZy5jYXAgPSAiU0FNUExFRDogSGl0cyB2cyBjb250cm9scyJ9CmhlYXRfaGl0c19zYW1wbGUgPSBrc19zYW1wbGVfZ2cgJT4lIAogIGRwbHlyOjpmaWx0ZXIoaGl0X2NvbnRyb2wgPT0gImhpdCIpICU+JSAKICBnZ3Bsb3QoKSArCiAgICBnZW9tX3RpbGUoYWVzKEEsIEIsIGZpbGwgPSBrc19QKSkgKwogICAgc2NhbGVfZmlsbF92aXJpZGlzX2MoKSArCiAgICBjb29yZF9maXhlZCgpICsKICAgIHhsYWIoTlVMTCkgKwogICAgeWxhYihOVUxMKSArCiAgICBsYWJzKGZpbGwgPSAiS1MtdGVzdFxuLWxvZyhQKSIpCgpoZWF0X2NvbnRyb2xzX3NhbXBsZSA9IGtzX3NhbXBsZV9nZyAlPiUgCiAgZHBseXI6OmZpbHRlcihoaXRfY29udHJvbCA9PSAiY29udHJvbCIpICU+JSAKICBnZ3Bsb3QoKSArCiAgICBnZW9tX3RpbGUoYWVzKEEsIEIsIGZpbGwgPSBrc19QKSkgKwogICAgc2NhbGVfZmlsbF92aXJpZGlzX2Mob3B0aW9uID0gIm1hZ21hIikgKwogICAgY29vcmRfZml4ZWQoKSArCiAgICB4bGFiKE5VTEwpICsKICAgIHlsYWIoTlVMTCkgKwogICAgbGFicyhmaWxsID0gIktTLXRlc3Rcbi1sb2coUCkiKQogICAKaGVhdF9oaXRzX3NhbXBsZQpoZWF0X2NvbnRyb2xzX3NhbXBsZQpgYGAKCmBgYHtyLCBldmFsID0gRiwgZWNobyA9IEZ9CmhlYXRfaGl0c19zYW1wbGUKYGBgCgpgYGB7ciwgZXZhbCA9IEZ9Cmdnc2F2ZShoZXJlKCJwbG90cyIsICIyMDIxMDEyN19rc19oZWF0bWFwcyIsICIyMDIxMDEyN19oaXRzX3NhbXBsZS5wbmciKSwKICAgICAgIGRldmljZSA9ICJwbmciLAogICAgICAgdW5pdHMgPSAiY20iLAogICAgICAgZHBpID0gNDAwLAogICAgICAgaGVpZ2h0ID0gMTIsCiAgICAgICB3aWR0aCA9IDIwKQpgYGAKCmBgYHtyLCBldmFsID0gRiwgZWNobyA9IEZ9CmhlYXRfY29udHJvbHNfc2FtcGxlCmBgYAoKYGBge3IsIGV2YWwgPSBGfQpnZ3NhdmUoaGVyZSgicGxvdHMiLCAiMjAyMTAxMjdfa3NfaGVhdG1hcHMiLCAiMjAyMTAxMjdfY29udHJvbHNfc2FtcGxlLnBuZyIpLAogICAgICAgZGV2aWNlID0gInBuZyIsCiAgICAgICB1bml0cyA9ICJjbSIsCiAgICAgICBkcGkgPSA0MDAsCiAgICAgICBoZWlnaHQgPSAxMiwKICAgICAgIHdpZHRoID0gMjApCmBgYAoKIyMjIENvbXBhcmUgZWZmZWN0cyBvZiBzYW1wbGluZyBvbiBQLXZhbHVlcwoKYGBge3IsIGZpZy5zaG93PSJob2xkIiwgb3V0LndpZHRoPSc1MCUnLCBmaWcuY2FwID0gIkhpdHM6IGFsbCB2cyBzYW1wbGUifQpoZWF0X2hpdHNfYWxsCmhlYXRfaGl0c19zYW1wbGUKYGBgCgpgYGB7ciwgZmlnLnNob3c9ImhvbGQiLCBvdXQud2lkdGg9JzUwJScsIGZpZy5jYXAgPSAiQ29udHJvbHM6IGFsbCB2cyBzYW1wbGUifQpoZWF0X2NvbnRyb2xzX2FsbApoZWF0X2NvbnRyb2xzX3NhbXBsZQpgYGAKCiMjIFRyeSBkaWZmZXJlbnQgd2F5cyBvZiB0ZXN0aW5nIGRpZmZlcmVuY2VzCgojIyMgUmVhZCBpbiAqRnN0KiByZXN1bHRzCmBgYHtyLCBtZXNzYWdlID0gRn0KZnN0X291dCA9IHJlYWRyOjpyZWFkX2NzdihoZXJlOjpoZXJlKCJkYXRhIiwgIjIwMjEwMTI3X3Jlc3VsdHMvMjAyMTAxMjhfZnN0LmNzdiIpKQpmc3Rfb3V0JHBoZW5vdHlwZSA8LSBmYWN0b3IoZnN0X291dCRwaGVub3R5cGUsIGxldmVscyA9IHRyYWl0X2xldmVsc192ZXJiWy0xXSkKCmBgYAoKIyMjIFJ1biBLb2xtb2dvcm92LVNtaXJub3YgVGVzdHMKCkZyb20gdGhlIGhlbHAgcGFnZXMgb2YgYGtzLnRlc3RgOgoKPiBUaGUgcG9zc2libGUgdmFsdWVzICJgdHdvLnNpZGVkYCIsICJgbGVzc2AiIGFuZCAiYGdyZWF0ZXJgIiBvZiBhbHRlcm5hdGl2ZSBzcGVjaWZ5IHRoZSBudWxsIGh5cG90aGVzaXMgdGhhdCB0aGUgdHJ1ZSBkaXN0cmlidXRpb24gZnVuY3Rpb24gb2YgYHhgIGlzIGVxdWFsIHRvLCBub3QgbGVzcyB0aGFuIG9yIG5vdCBncmVhdGVyIHRoYW4gdGhlIGh5cG90aGVzaXplZCBkaXN0cmlidXRpb24gZnVuY3Rpb24gKG9uZS1zYW1wbGUgY2FzZSkgb3IgdGhlIGRpc3RyaWJ1dGlvbiBmdW5jdGlvbiBvZiBgeWAgKHR3by1zYW1wbGUgY2FzZSksIHJlc3BlY3RpdmVseS4gVGhpcyBpcyBhIGNvbXBhcmlzb24gb2YgY3VtdWxhdGl2ZSBkaXN0cmlidXRpb24gZnVuY3Rpb25zLCBhbmQgdGhlIHRlc3Qgc3RhdGlzdGljIGlzIHRoZSBtYXhpbXVtIGRpZmZlcmVuY2UgaW4gdmFsdWUsIHdpdGggdGhlIHN0YXRpc3RpYyBpbiB0aGUgYCJncmVhdGVyImAgYWx0ZXJuYXRpdmUgYmVpbmcgKkReKyA9IG1heFtGX3godSkgLSBGX3kodSldKi4gVGh1cyBpbiB0aGUgdHdvLXNhbXBsZSBjYXNlIGBhbHRlcm5hdGl2ZSA9ICJncmVhdGVyImAgaW5jbHVkZXMgZGlzdHJpYnV0aW9ucyBmb3Igd2hpY2ggeCBpcyBzdG9jaGFzdGljYWxseSBzbWFsbGVyIHRoYW4geSAodGhlIENERiBvZiB4IGxpZXMgYWJvdmUgYW5kIGhlbmNlIHRvIHRoZSBsZWZ0IG9mIHRoYXQgZm9yIHkpLCBpbiBjb250cmFzdCB0byB0LnRlc3Qgb3Igd2lsY294LnRlc3QuCgpgYGB7ciwgd2FybmluZyA9IEZ9CmtzX291dCA9IHNwbGl0KGZzdF9vdXQsIGYgPSBmc3Rfb3V0JGhpdF9jb250cm9sKQprc19vdXQgPSBsYXBwbHkoa3Nfb3V0LCBmdW5jdGlvbihkYXRhc2V0KXsKICBzcGxpdF9kYXRhID0gc3BsaXQoZGF0YXNldCwgZiA9IGRhdGFzZXQkcGhlbm90eXBlKQogICMgcnVuIHBhaXJ3aXNlIEtTIHRlc3RzCiAga3Nfb3V0ID0gbGFwcGx5KHNwbGl0X2RhdGEsIGZ1bmN0aW9uKHRyYWl0X2EpewogICAgb3V0ID0gbGFwcGx5KHNwbGl0X2RhdGEsIGZ1bmN0aW9uKHRyYWl0X2IpewogICAgICAjICJ0d28uc2lkZWQiCiAgICAgIHJlc3VsdHNfdHdvX3NpZGVkID0ga3MudGVzdCh0cmFpdF9hJEZzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyYWl0X2IkRnN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWx0ZXJuYXRpdmUgPSAidHdvLnNpZGVkIikgICAgICAKICAgICAgIyAibGVzcyIgdGVzdAogICAgICByZXN1bHRzX2xlc3MgPSBrcy50ZXN0KHRyYWl0X2EkRnN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyYWl0X2IkRnN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFsdGVybmF0aXZlID0gImxlc3MiKQogICAgICAjICJncmVhdGVyIiB0ZXN0CiAgICAgIHJlc3VsdHNfZ3JlYXRlciA9IGtzLnRlc3QodHJhaXRfYSRGc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhaXRfYiRGc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWx0ZXJuYXRpdmUgPSAiZ3JlYXRlciIpCiAgICAgICMgTWFubi1XaGl0bmV5IAogICAgICByZXN1bHRzX213ID0gd2lsY294LnRlc3QodHJhaXRfYSRGc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFpdF9iJEZzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFsdGVybmF0aXZlID0gInR3by5zaWRlZCIpCiAgICAgICMgYmluZCBpbnRvIERGCiAgICAgIGRmX291dCA9IGMocmVzdWx0c190d29fc2lkZWRbWyJzdGF0aXN0aWMiXV0sCiAgICAgICAgICAgICAgICAgIlBfVFdPX1NJREVEIiA9IHJlc3VsdHNfdHdvX3NpZGVkW1sicC52YWx1ZSJdXSwKICAgICAgICAgICAgICAgICByZXN1bHRzX2xlc3NbWyJzdGF0aXN0aWMiXV0sCiAgICAgICAgICAgICAgICAgIlBfTEVTUyIgPSByZXN1bHRzX2xlc3NbWyJwLnZhbHVlIl1dLAogICAgICAgICAgICAgICAgIHJlc3VsdHNfZ3JlYXRlcltbInN0YXRpc3RpYyJdXSwKICAgICAgICAgICAgICAgICAiUF9HUkVBVEVSIiA9IHJlc3VsdHNfZ3JlYXRlcltbInAudmFsdWUiXV0sCiAgICAgICAgICAgICAgICAgcmVzdWx0c19td1tbInN0YXRpc3RpYyJdXSwKICAgICAgICAgICAgICAgICAiUF9NVyIgPSByZXN1bHRzX213W1sicC52YWx1ZSJdXSkKCiAgICAgIHJldHVybihkZl9vdXQpCiAgICAgIHJldHVybihyZXN1bHRzX213KQogICAgfSkgJT4lIAogICAgICBkcGx5cjo6YmluZF9yb3dzKC5pZCA9ICJCIikKICB9KSAlPiUgCiAgICBkcGx5cjo6YmluZF9yb3dzKC5pZCA9ICJBIikKfSkgJT4lIAogIGRwbHlyOjpiaW5kX3Jvd3MoLmlkID0gIkhJVF9DT05UUk9MIikgJT4lIAogIGRwbHlyOjptdXRhdGUoYWNyb3NzKGMoIkEiLCAiQiIpLAogICAgICAgICAgICAgICAgICAgICAgIH5mYWN0b3IoLngsIGxldmVscyA9IHRyYWl0X2xldmVsc192ZXJiKSkpCgoKYGBgCgojIyMgUGxvdCBLUy10ZXN0IEQKClNob3cgY29ycmVsYXRpb24KCmBgYHtyfQpwbHQgPSBrc19vdXQgJT4lIAogIGdncGxvdCgpICsKICAgIGdlb21fcG9pbnQoYWVzKGBEXitgLCBgRF4tYCwgY29sb3VyID0gSElUX0NPTlRST0wsCiAgICAgICAgICAgICAgICAgICB0ZXh0ID0gcGFzdGUoIlRyYWl0IEE6ICIsIEEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxicj5UcmFpdCBCOiAiLCBCKSkpICsKICAgIHRoZW1lX2J3KCkgKwogICAgY29vcmRfZml4ZWQoKQoKZ2dwbG90bHkocGx0KQoKYGBgCgoKCmBgYHtyfQpnZ3Bsb3RseShwbHQpICU+JQogIGV4cG9ydChmaWxlID0gaGVyZTo6aGVyZSgicGxvdHMiLCAiMjAyMTAxMjlfS1MtRC5zdmciKSwKICAgICAgICAgc2VsZW5pdW0gPSBSU2VsZW5pdW06OnJzRHJpdmVyKGJyb3dzZXIgPSAiZmlyZWZveCIpKQpgYGAKCiMjIyBQbG90IE1XCgpgYGB7cn0KaGVhdF9oaXRzX3NhbXBsZSA9IGtzX291dCAlPiUgCiAgZHBseXI6OmZpbHRlcihISVRfQ09OVFJPTCA9PSAiaGl0IikgJT4lIAogIGdncGxvdCgpICsKICAgIGdlb21fdGlsZShhZXMoQSwgQiwgZmlsbCA9IC1sb2cxMChQX01XKSkpICsKICAgIHNjYWxlX2ZpbGxfdmlyaWRpc19jKCkgKwogICAgY29vcmRfZml4ZWQoKSArCiAgICB4bGFiKE5VTEwpICsKICAgIHlsYWIoTlVMTCkgKwogICAgbGFicyhmaWxsID0gIk1XLXRlc3Rcbi1sb2coVykiKQoKZ2dzYXZlKGhlcmUoInBsb3RzIiwgIjIwMjEwMTI3X2tzX2hlYXRtYXBzIiwgIjIwMjEwMTI3X2NvbnRyb2xzX3NhbXBsZS5wbmciKSwKICAgICAgIGRldmljZSA9ICJwbmciLAogICAgICAgdW5pdHMgPSAiY20iLAogICAgICAgZHBpID0gNDAwLAogICAgICAgaGVpZ2h0ID0gMTIsCiAgICAgICB3aWR0aCA9IDIwKQoKaGVhdF9jb250cm9sc19zYW1wbGUgPSBrc19vdXQgJT4lIAogIGRwbHlyOjpmaWx0ZXIoSElUX0NPTlRST0wgPT0gImNvbnRyb2wiKSAlPiUgCiAgZ2dwbG90KCkgKwogICAgZ2VvbV90aWxlKGFlcyhBLCBCLCBmaWxsID0gLWxvZzEwKFBfTVcpKSkgKwogICAgc2NhbGVfZmlsbF92aXJpZGlzX2Mob3B0aW9uID0gIm1hZ21hIikgKwogICAgY29vcmRfZml4ZWQoKSArCiAgICB4bGFiKE5VTEwpICsKICAgIHlsYWIoTlVMTCkgKwogICAgbGFicyhmaWxsID0gIk1XLXRlc3Rcbi1sb2coVykiKQoKZ2dzYXZlKGhlcmUoInBsb3RzIiwgIjIwMjEwMTI3X2tzX2hlYXRtYXBzIiwgIjIwMjEwMTI3X2NvbnRyb2xzX3NhbXBsZS5wbmciKSwKICAgICAgIGRldmljZSA9ICJwbmciLAogICAgICAgdW5pdHMgPSAiY20iLAogICAgICAgZHBpID0gNDAwLAogICAgICAgaGVpZ2h0ID0gMTIsCiAgICAgICB3aWR0aCA9IDIwKQogICAKaGVhdF9oaXRzX3NhbXBsZQpoZWF0X2NvbnRyb2xzX3NhbXBsZQpgYGAKCiMjIENhbGN1bGF0ZSAqRnN0KiBmb3IgMTAwayByYW5kb20gU05QcyB0byBjb21wYXJlIHRvIHRoZXNlIGRpc3RyaWJ1dGlvbnMKCiMjIyBQdWxsIHJhbmRvbSBsaW5lcyBmcm9tIDFrZyBhbGZyZXEgZmlsZQoKYGBge2Jhc2h9CiMgT24gY2x1c3RlciAKCmxpc3RfaW49Li4vYmlnX2RhdGEvMjAyMTAxMjVfYWxmcmVxc19hbGxfYmlubmVkL2FsbC5hZnJlcQpsaXN0X291dD1kYXRhLzIwMjEwMTI5X3JhbmRvbV8xMDBrX3NucHMubGlzdApyZWY9Li4vcmVmcy9oczM3ZDUuZmEuZ3oKaW5fdmNmPS4uL3ZjZnMvMWtnX2FsbC52Y2YuZ3oKb3V0X3ZjZj0uLi92Y2ZzLzFrZ18xMDBrX3JuZG0udmNmLmd6Cgpjb25kYSBhY3RpdmF0ZSBmc3RfZW52X3JoZWwgCgojIGNyZWF0ZSBmdW5jdGlvbiB0byBnZXQgcmFuZG9tIHNlZWQKZ2V0X3NlZWRlZF9yYW5kb20oKQp7CiAgc2VlZD0iJDEiCiAgb3BlbnNzbCBlbmMgLWFlcy0yNTYtY3RyIC1wYXNzIHBhc3M6IiRzZWVkIiAtbm9zYWx0IFwKICAgIDwvZGV2L3plcm8gMj4vZGV2L251bGwKfQoKIyBtYWtlIGxpc3QKYXdrICd7cHJpbnQgJDJ9JyAkbGlzdF9pbiB8XAogIHRhaWwgLW4rMiB8XAogIHNodWYgLW4gMTAwMDAwIFwKICAtLXJhbmRvbS1zb3VyY2U9PChnZXRfc2VlZGVkX3JhbmRvbSA0NTQpIFwKICAgID4gJGxpc3Rfb3V0CiAgICAKIyBleHRyYWN0IGZyb20gMUtHCmdhdGsgU2VsZWN0VmFyaWFudHMgXAogIC1SICRyZWYgXAogIC1WICRpbl92Y2YgXAogIC0ta2VlcC1pZHMgJGxpc3Rfb3V0IFwKICAtTyAkb3V0X3ZjZgpgYGAKCiMjIyBSdW4gYHBlZ2FzYCB0byBnZXQgRnN0CgpgYGB7ciwgZXZhbCA9IEZ9CiMgT24gY2x1c3RlcgpsaWJyYXJ5KGhlcmUpCnNvdXJjZShoZXJlOjpoZXJlKCJjb2RlIiwgInNjcmlwdHMiLCAic291cmNlLlIiKSkKCiMgU2V0IHZhcmlhYmxlcwppbl92Y2Y9aGVyZTo6aGVyZSgiLi4iLCAidmNmcyIsICIxa2dfMTAwa19ybmRtLnZjZi5neiIpCnNhbXBsZXNfZmlsZSA9IGhlcmU6OmhlcmUoImRhdGEiLCAiMjAxMzA2MDZfc2FtcGxlX2luZm8ueGxzeCIpCm91dF9maWxlPWhlcmU6OmhlcmUoImRhdGEiLCAiMjAyMTAxMjlfMTAwa19ybmRtX2ZzdC50eHQiKQoKIyBSZWFkIGluIGBtZXRhYCBmaWxlCm1ldGEgPSByZWFkeGw6OnJlYWRfeGxzeChzYW1wbGVzX2ZpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICBzaGVldCA9ICJTYW1wbGUgSW5mbyIpICU+JQogIGRwbHlyOjpzZWxlY3QoU2FtcGxlLCBQb3B1bGF0aW9uLCBHZW5kZXIpCgojIFJlYWQgVkNGIAp2Y2Zfb3V0IDwtIHBlZ2FzOjpyZWFkLnZjZihpbl92Y2YsIHRvID0gMTAwMDAwKQoKIyBDcmVhdGUgdmVjdG9yIG9mIHBvcHVsYXRpb25zCnBvcHVsYXRpb25zID0gdW5saXN0KGxhcHBseShyb3duYW1lcyh2Y2Zfb3V0KSwgZnVuY3Rpb24oc2FtcGxlKXsKICBtZXRhJFBvcHVsYXRpb25bbWV0YSRTYW1wbGUgPT0gc2FtcGxlXQp9KSkKCiMgR2VuZXJhdGUgRnN0IHN0YXRzCmZzdF9vdXQgPC0gYXMuZGF0YS5mcmFtZShwZWdhczo6RnN0KHZjZl9vdXQsIHBvcCA9IHBvcHVsYXRpb25zKSkKZnN0X291dCRzbnAgPC0gcm93bmFtZXMoZnN0X291dCkKIyByZW1vdmUgTkFzCmZzdF9vdXQgID0gZnN0X291dCAlPiUgCiAgdGlkeXI6OmRyb3BfbmEoKSAKIyA5OTY4MiByZW1haW5pbmcKICAKIyBTZXQgcGhlbm90eXBlCmZzdF9vdXQkcGhlbm90eXBlIDwtICJSYW5kb20iCgojIFNhdmUgZmlsZQpyZWFkcjo6d3JpdGVfdHN2KGZzdF9vdXQsIG91dF9maWxlKQpgYGAKCiMjIyBSZWFkIGluCgpgYGB7cn0KIyByZWFkCmZzdF9vdXQgPSByZWFkcjo6cmVhZF9jc3YoaGVyZTo6aGVyZSgiZGF0YSIsICIyMDIxMDEyN19yZXN1bHRzLzIwMjEwMTI4X2ZzdC5jc3YiKSkKZnN0X3JhbmRvbSA9IHJlYWRyOjpyZWFkX3RzdihoZXJlOjpoZXJlKCJkYXRhIiwgIjIwMjEwMTI5XzEwMGtfcm5kbV9mc3QudHh0IikpICU+JSAKICBkcGx5cjo6bXV0YXRlKGhpdF9jb250cm9sID0gImhpdCIpCgojIGJpbmQKZnN0X291dCA9IGRwbHlyOjpiaW5kX3Jvd3MoZnN0X291dCwgZnN0X3JhbmRvbSkKCiMgZmFjdG9yCmZzdF9vdXQkcGhlbm90eXBlIDwtIGZhY3Rvcihmc3Rfb3V0JHBoZW5vdHlwZSwgbGV2ZWxzID0gdHJhaXRfbGV2ZWxzX3ZlcmIpCmBgYAoKIyMjIFBsb3QgCgojIyMjICpGc3QqIGRlbnNpdHkKCiMjIyMjIEZhY2V0cwoKYGBge3J9CmZzdF9vdXQgJT4lIAogIGRwbHlyOjpmaWx0ZXIoaGl0X2NvbnRyb2wgPT0gImhpdCIpICU+JSAKICBnZ3Bsb3QoYWVzKEZzdCwgZmlsbCA9IHBoZW5vdHlwZSkpICsKICAgIGdlb21fZGVuc2l0eSgpICsKICAgIGxhYnMoZmlsbCA9ICJQaGVub3R5cGUiKSArCiAgICBmYWNldF93cmFwKH5waGVub3R5cGUpICsKICAgIHlsYWIoIkRlbnNpdHkiKSArCiAgICB0aGVtZV9idygpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbF9wcmltYXJ5KSArCiAgICBndWlkZXMoZmlsbCA9IEYpCgpgYGAKCiMjIyMjIFJpZGdlcwoKYGBge3IsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRiwgZmlnLnNob3c9ImhvbGQiLCBvdXQud2lkdGg9JzUwJScsIGZpZy5jYXAgPSAiSGl0cyB2cyBjb250cm9scyJ9CmZzdF9vdXQgJT4lIAogIGRwbHlyOjpmaWx0ZXIoaGl0X2NvbnRyb2wgPT0gImhpdCIpICU+JSAKICBkcGx5cjo6bXV0YXRlKHBoZW5vdHlwZSA9IGZhY3RvcihwaGVub3R5cGUsIGxldmVscyA9IHJldih0cmFpdF9sZXZlbHNfdmVyYikpKSAlPiUgCiAgZ2dwbG90KCkgKwogICAgZ2VvbV9kZW5zaXR5X3JpZGdlczIobWFwcGluZyA9IGFlcyh4ID0gRnN0LCB5ID0gcGhlbm90eXBlLCBmaWxsID0gcGhlbm90eXBlKSwKICAgICAgICAgICAgICAgICAgICAgICAgIHNjYWxlID0gMikgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsX3ByaW1hcnkpICsKICAgIHlsYWIobGFiZWwgPSBOVUxMKSArCiAgICB0aGVtZV9idygpICsKICAgIGd1aWRlcyhmaWxsID0gRikgKwogICAgc2NhbGVfeV9kaXNjcmV0ZShleHBhbmQgPSBleHBhbmRfc2NhbGUoYWRkID0gYygwLjIsIDIuMykpKQpgYGAKCiMgRmluYWwKCiMjIEdldCBtZWRpYW4gKkZzdCogYW5kIDAuOSBwZXJjZW50aWxlCgojIyMgUmVhZCBpbiBkYXRhCgpgYGB7cn0KIyBSZWFkIGluIGRhdGEKZnN0X291dCA9IHJlYWRyOjpyZWFkX2NzdihoZXJlOjpoZXJlKCJkYXRhIiwgIjIwMjEwMTI3X3Jlc3VsdHMvMjAyMTAxMjhfZnN0LmNzdiIpKQpmc3Rfb3V0JHBoZW5vdHlwZSA8LSBmYWN0b3IoZnN0X291dCRwaGVub3R5cGUsIGxldmVscyA9IHRyYWl0X2xldmVsc192ZXJiKQpmc3Rfb3V0JGhpdF9jb250cm9sID0gZmFjdG9yKGZzdF9vdXQkaGl0X2NvbnRyb2wsIGxldmVscyA9IGhpdF9jb250cm9sX2xldmVscykKCiMgU3BsaXQKZnN0X2xpc3QgPSBzcGxpdChmc3Rfb3V0LCBmID0gZnN0X291dCRoaXRfY29udHJvbCkKZnN0X2xpc3QgPSBsYXBwbHkoZnN0X2xpc3QsIGZ1bmN0aW9uKGRhdGFzZXQpewogIHNwbGl0KGRhdGFzZXQsIGYgPSBkYXRhc2V0JHBoZW5vdHlwZSkKfSkKYGBgCgojIyMgTWVkaWFuIGFuZCAwLjkgcGVyY2VudGlsZQoKRnJvbSB0aGUgYGhlbHBgIHBhZ2Ugb2YgYHdpbGNveG9uLnRlc3RgOgoKPiBOb3RlIHRoYXQgaW4gdGhlIHR3by1zYW1wbGUgY2FzZSB0aGUgZXN0aW1hdG9yIGZvciB0aGUgZGlmZmVyZW5jZSBpbiBsb2NhdGlvbiBwYXJhbWV0ZXJzIGRvZXMgbm90IGVzdGltYXRlIHRoZSBkaWZmZXJlbmNlIGluIG1lZGlhbnMgKGEgY29tbW9uIG1pc2NvbmNlcHRpb24pIGJ1dCByYXRoZXIgdGhlIG1lZGlhbiBvZiB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIGEgc2FtcGxlIGZyb20geCBhbmQgYSBzYW1wbGUgZnJvbSB5LgoKYGBge3J9Cm13X291dCA9IHNwbGl0KGZzdF9vdXQsIGYgPSBmc3Rfb3V0JGhpdF9jb250cm9sKQptd19vdXQgPSBsYXBwbHkobXdfb3V0LCBmdW5jdGlvbihkYXRhc2V0KXsKICBzcGxpdF9kYXRhID0gc3BsaXQoZGF0YXNldCwgZiA9IGRhdGFzZXQkcGhlbm90eXBlKQogIAogIG91dCA9IGxhcHBseShzcGxpdF9kYXRhLCBmdW5jdGlvbihwaGVubyl7CiAgICBzdGF0cyA9IGxpc3QoKQogICAgCiAgICAjIEdldCBtZWRpYW4gYW5kIDkwIHBlcmNlbnRpbGUKICAgIHN0YXRzW1sic3RhdHMiXV0gPSBjKCJtZWRpYW4iID0gbWVkaWFuKHBoZW5vJEZzdCksCiAgICAgICAgICAgICAgICAgICAgICAgICBxdWFudGlsZShwaGVubyRGc3QsIHByb2JzID0gMC45KSkKICAgIAogICAgIyBHZXQgTWFubi1XaGl0bmV5IHJlc3VsdHMKICAgIHN0YXRzW1sibXdfb3V0Il1dID0gd2lsY294LnRlc3QoeCA9IHNwbGl0X2RhdGFbWyJIZWlnaHQiXV0kRnN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gcGhlbm8kRnN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbHRlcm5hdGl2ZSA9ICJsZXNzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFpcmVkID0gRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uZi5pbnQgPSBUKQogICAgCiAgICAjIEdldCBLUyByZXN1bHRzCiAgICBzdGF0c1tbImtzX291dCJdXSA9IGtzLnRlc3QoeCA9IHNwbGl0X2RhdGFbWyJIZWlnaHQiXV0kRnN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBwaGVubyRGc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWx0ZXJuYXRpdmUgPSAiZ3JlYXRlciIpCgogICAgcmV0dXJuKHN0YXRzKQogIH0pIyAlPiUgCiAgICAjZHBseXI6OmJpbmRfcm93cyguaWQgPSAicGhlbm90eXBlIikKICAKICAKICByZXR1cm4ob3V0KQoKfSkKCndpbGNveC50ZXN0KGZzdF9saXN0JGhpdCRIZWlnaHQkRnN0LCBmc3RfbGlzdCRoaXQkSGVpZ2h0JEZzdCwKICAgICAgICAgICAgYWx0ZXJuYXRpdmUgPSAibGVzcyIpCgprcy50ZXN0KGZzdF9saXN0JGhpdCRIZWlnaHQkRnN0LCBmc3RfbGlzdCRoaXQkYEVkdWNhdGlvbmFsIGF0dGFpbm1lbnRgJEZzdCkKCiMgQ29sbGFwc2UgaW50byBERgptd19nZyA9IGxhcHBseShtd19vdXQsIGZ1bmN0aW9uKGRhdGFzZXQpewogIG91dCA9IGxhcHBseShkYXRhc2V0LCBmdW5jdGlvbihwaGVubyl7CiAgICAjIFB1dCBrZXkgZGF0YSBpbnRvIGRhdGEgZnJhbWUKICAgIGRmID0gYygiTUVESUFOIiA9IHBoZW5vW1sic3RhdHMiXV1bWyJtZWRpYW4iXV0sCiAgICAgICAgICAgIjkwJSIgPSBwaGVub1tbInN0YXRzIl1dW1siOTAlIl1dLAogICAgICAgICAgIHBoZW5vW1sibXdfb3V0Il1dJHN0YXRpc3RpYywKICAgICAgICAgICAiUF9NVyIgPSBwaGVub1tbIm13X291dCJdXSRwLnZhbHVlLAogICAgICAgICAgICJDT05GX0xPV0VSIiA9IHBoZW5vW1sibXdfb3V0Il1dJGNvbmYuaW50WzFdLAogICAgICAgICAgICJDT05GX1VQUEVSIiA9IHBoZW5vW1sibXdfb3V0Il1dJGNvbmYuaW50WzJdLAogICAgICAgICAgIHBoZW5vW1sia3Nfb3V0Il1dJHN0YXRpc3RpYywKICAgICAgICAgICAiUF9LUyIgPSBwaGVub1tbImtzX291dCJdXSRwLnZhbHVlKQoKICAgIHJldHVybihkZikKICB9KSAlPiUgCiAgICBkcGx5cjo6YmluZF9yb3dzKC5pZCA9ICJQSEVOTyIpCiAgCiAgcmV0dXJuKG91dCkKfSkgJT4lIAogIGRwbHlyOjpiaW5kX3Jvd3MoLmlkID0gIkhJVF9DT05UUk9MIikKCiMgRmFjdG9yCm13X2dnJFBIRU5PIDwtIGZhY3Rvcihtd19nZyRQSEVOTywgbGV2ZWxzID0gdHJhaXRfbGV2ZWxzX3ZlcmIpCm13X2dnJEhJVF9DT05UUk9MID0gZmFjdG9yKG13X2dnJEhJVF9DT05UUk9MLCBsZXZlbHMgPSBoaXRfY29udHJvbF9sZXZlbHMpCmBgYAoKUGxvdAoKYGBge3J9Cm13X2dnICU+JSAKICBnZ3Bsb3QoKSArCiAgICBnZW9tX2NvbChhZXMoUEhFTk8sIC1sb2cxMChQX01XKSwgZmlsbCA9IFBIRU5PKSkgKwogICAgZmFjZXRfd3JhcCh+SElUX0NPTlRST0wpICsgCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxfcHJpbWFyeSkgKwogICAgdGhlbWVfYncoKSArCiAgICBnZ3RpdGxlKCJNYW5uLVdoaXRuZXkgcC12YWx1ZXMiKQoKYGBgCmBgYHtyfQptd19nZyAlPiUgCiAgZ2dwbG90KCkgKwogICAgZ2VvbV9jb2woYWVzKFBIRU5PLCAtbG9nMTAoUF9LUyksIGZpbGwgPSBQSEVOTykpICsKICAgIGZhY2V0X3dyYXAofkhJVF9DT05UUk9MKSArIAogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsX3ByaW1hcnkpICsKICAgIHRoZW1lX2J3KCkgKwogICAgZ2d0aXRsZSgiS1MtdGVzdCBwLXZhbHVlcyIpCgpgYGAKCg==